Compare commits

...

118 Commits

Author SHA1 Message Date
Martin Staffa 30cd764b6d docs(changelog): move bootstrap fixes to Bug Fix section 2017-03-08 12:44:24 +01:00
Martin Staffa 38b75cdb2d docs(changelog): add release notes for 1.6.3 and 1.6.2 2017-03-08 12:27:00 +01:00
Pablo Targa 6cb8b39af8 docs(ngAnimate): update staggering config for use with css animations
Closes #15743
2017-03-07 20:25:09 +01:00
diegomrsantos ebaa336614 docs(guide/migration): add info for 1.4 (ng)Pattern BC
Breaking change was introduced in commit 0e001084ff.
This content being included in the migration guide is taken from the commit message of commit 0e001084ff.

Closes #15758
Closes #15765
2017-03-07 20:24:59 +01:00
mohamed amr ef5f567f91 test(errorHandlingConfig): add tests for errorHandlingConfig() (independent of minErr)
Closes #15770
2017-03-05 00:46:14 +02:00
Michał Gołębiowski 64e5afc478 fix($log): don't parse error stacks manually outside of IE/Edge
IE/Edge display errors in such a way that it requires the user to click in
4 places to see the stack trace. There is no way to feature-detect it so
there's a chance of the user agent sniffing to go wrong but since it's only
about logging, this shouldn't break apps. Other browsers display errors in
a sensible way and some of them map stack traces along source maps if available
so it makes sense to let browsers display it as they want.

Fixes #15590
Closes #15767
2017-03-03 11:41:02 +01:00
Peter Bacon Darwin 1e582e4fa4 feat(info): add angularVersion info to each module
You can now check what version of AngularJS a core module is designed for:

```
var angularVersion = $injector.modules['myModule'].info().angularVersion;
```
2017-03-02 08:25:16 +00:00
Peter Bacon Darwin 7421235f24 feat($injector): add new modules property
The `modules` property is a hash of the modules loaded into the injector
at bootstrap time. This can be used to access the module's info.
2017-03-02 08:25:09 +00:00
Peter Bacon Darwin 09ba69078d feat(Module): add info() method
The new `info()` method lets developers store arbitrary information about
their module for consumption later.

Closes #15225
2017-03-02 08:25:01 +00:00
Peter Bacon Darwin 3536e83d8a fix(Angular): do not autobootstrap if the src exists but is empty
In Chrome an empty `src` attribute will be ignored, but in Firefox it seems
happy to prepend the `base[href]` and try to load whatever that is.
2017-02-27 20:47:04 +00:00
Georgios Kalpakas 3bb1dd5d7f fix($sanitize): prevent clobbered elements from freezing the browser
Closes #15699
2017-02-24 17:42:14 +00:00
Peter Bacon Darwin 95f964b827 fix(Angular): do not auto bootstrap if the currentScript has been clobbered 2017-02-24 15:04:10 +00:00
Peter Bacon Darwin c8f78a8ca9 fix(Angular): do not auto bootstrap if the script source is bad and inside SVG 2017-02-24 15:04:03 +00:00
Peter Bacon Darwin f34d48087b test(Angular): refactor auto bootstrap tests 2017-02-24 15:03:56 +00:00
Martin Staffa 2c9ecd01b1 docs($compile): clarify to which element scope isolation applies
Closes #13556
2017-02-22 21:08:52 +01:00
Georgios Kalpakas a584fb6e15 fix($animate): reset classNameFilter to null when a disallowed RegExp is used
Closes #14913
2017-02-17 11:33:13 +02:00
Georgios Kalpakas 1f13313f40 fix($animate): improve detection on ng-animate in classNameFilter RegExp
Fixes #14806
2017-02-17 11:33:12 +02:00
Martin Staffa 5b60303781 chore(docs-app): add debounce to search input
This fixes issues where the search results do not correctly reflect
the search query. This happens in Firefox when you enter a search query
very rapidly.
There is probably an issue with the async behavior of the search / webworker,
so this is just a workaround.
2017-02-16 14:28:08 +01:00
Martin Staffa 10e2552a7d chore(docs-app): update links in header menu
They are now in the same order as angularjs.org

Closes #14351
2017-02-16 14:28:08 +01:00
Martin Staffa ef48b0aa55 chore(docs-app): update the header style
Also adds a new fixed strip / bar about the AngularJS version

Closes #14963
Closes #15670
2017-02-16 14:28:08 +01:00
Keith Walsh f57872bca0 docs($resource): add minor clarification
Closes #15711
2017-02-16 12:00:45 +02:00
Martin Staffa 2deaf2877e docs(select, ngOptions): add ngAttrSize as argument
Closes #1619
2017-02-15 15:28:08 +01:00
Martin Staffa 7a146c9cd5 docs(ngModelController): improve $formatters and $parsers info
Closes #11714
Closes #8194
2017-02-15 13:05:44 +01:00
Martin Staffa 2796ec172b docs(filterFilter): add note about self-referencing objects in array
Relate #6655, #6319
2017-02-15 12:21:58 +01:00
Martin Staffa 6997c1bf0c docs(guide/directive): clarify which type of matching directives support
Closes #15710
2017-02-15 12:21:22 +01:00
Georgii Dolzhykov 4a030f3834 refactor($rootScope): remove extraneous call to $parse in $evalAsync
Closes #15682
2017-02-09 23:39:56 +02:00
Michał Gołębiowski f78d8b8ff3 chore(jenkins): get rid of Opera from the Jenkins build script
The Opera launcher hasn't been installed for ages, but until Karma 1.4.0 the
error of Opera not being able to start was ignored. Karma has fixed the bug and
now Jenkins is failing.

This commit also removes Opera/Opera launcher mentions from the docs. We don't
support Opera officially anymore (it's sort-of supported via being based on
Blink).

Closes #15691
2017-02-09 12:59:43 +02:00
PRIJCK Frederik (FPRJ) f27d19ed60 fix(filterFilter): don't throw if key.charAt is not a function
Previously, when an object has keys which are not of type string, `filterFilter`
would throw an exception for trying to call `key.charAt()`, which is a string
method.

This commit checks whether `charAt` is defined before calling it.

Fixes #15644

Closes #15660
2017-02-08 23:18:01 +02:00
mohamed amr 4a5eaf7bec feat(errorHandlingConfig): make the depth for object stringification in errors configurable
Closes #15402
Closes #15433
2017-02-08 19:06:17 +02:00
Martin Staffa 8513674911 fix(select): add attribute "selected" for select[multiple]
This helps screen readers identify the selected options,
see #14419
2017-02-08 17:46:24 +01:00
Martin Staffa 97b74ad6fb fix(select): keep original selection when using shift to add options in IE/Edge
In IE9-11 + Edge, the selected options were previously incorrect under the following
circumstances:
- at least two options are selected
- shift+click or shift+down/up is used to add to the selection (any number of options)

In these cases, only the last of the previously selected options and the newly selected
options would be selected.

The problems seems to be that the render engine gets confused when an option that
already has selected = true gets selected = true set again.

Note that this is not testable via unit test because it's not possible to simulate
click / keyboard events on option elements (the events are delegated to the select element
change event), and the problem also doesn't appear when modifying the option elements directly
and then using the selectController API. It seems that this only happens when you manipulate the
select directly in the user interface.

Fixes #15675
Closes #15676
2017-02-08 17:46:21 +01:00
Martin Staffa a47ea79023 test($http): ensure json deserialization errors are forwarded to error handler
Since https://github.com/angular/angular.js/commit/e13eeabd7e34a78becec06cfbe72c23f2dcb85f9,
errors thrown from onFulfilled and onRejected handlers are passed to the regular http
error handlers. Before this, JSON deserialization errors lead to hard application errors, and could
not be handled by application code. This behavior was introduced in https://github.com/angular/angular.js/commit/7b6c1d08aceba6704a40302f373400aed9ed0e0b, and originally, a malformed JSON string was forwarded
as the data to the http success response handler.

This commit adds a specifc test case, even though the behavior is unlikely to break in the future without
a change in the $q rejection handling.

Related #11433
Closes #15689
2017-02-08 17:46:17 +01:00
Matt Lewis 5ca0de6487 fix($jsonpCallbacks): allow $window to be mocked in unit tests
Fixes #15685

Closes #15686
2017-02-08 18:35:55 +02:00
Georgios Kalpakas 50a449f053 chore(*): update changez-angular 2017-02-08 18:35:12 +02:00
Georgios Kalpakas d7422da7d7 test($resource): fix broken test
(Introduced while "cleaning up" the tests for in edfb691.)
2017-02-05 17:58:25 +02:00
Georgios Kalpakas c7cbc978c6 refactor(*): remove ignored expensiveChecks argument passed to $parse()
This is a follow-up to #15094.

Closes #15680
2017-02-05 15:39:10 +02:00
Kyle Wuolle 27146e8a7f fix($resource): do not swallow errors in success callback
Previously, errors thrown inside the `success` callback would be swallowed by a
noop `catch()` handler. The `catch()` handler was added in order to avoid an
unnecessary "Possibly Unhandled Rejection" error, in case the user provided an
`error` callback (which would handle request errors).

The handler was added too "eagrly" and as a result would swallow errors thrown
in the `success` callback, despite the fact that those errors would _not_ be
handled by the `error` callback.

This commit fixes this, by adding the `catch()` handler "lazily", only when it
is certain that a rejection will be handled by the `error` callback.

Fixes #15624

Closes #15628
2017-02-04 21:01:22 +02:00
Kindy Lin 5e418b1145 fix($parse): make sure ES6 object computed properties are watched
Add the missing watches for ES6 object computed properties which were
implemented in #14407.

Closes #15678
2017-02-04 16:18:21 +02:00
Dimitris Vardoulakis f4bb973eb7 refactor(*): avoid non-existent property warnings from Closure Compiler
Closes #15672
2017-02-02 22:38:27 +02:00
Chris 848857aa5b docs(misc/started): update Twitter handle (@angularjs --> @angular)
Closes #15671
2017-02-02 19:30:48 +02:00
Jessica Soltero ee8a05d3f1 docs(guide/expression): typo in one-time-binding
Closes #15668
2017-02-02 12:45:58 +02:00
Michał Gołębiowski 275ebbf0ec refactor($injector): remove the Chrome stringification hack
The Chrome stringification hack added in afcedff34c
is no longer needed. I verified that both of the commented out tests pass
on Chrome 56.
2017-02-01 15:02:52 +00:00
Michał Gołębiowski 0f23df4c06 chore(anchorScroll): remove a Jasmine toHaveBeenCalled workaround
The Jasmine fix landed long time ago and we've updated Jasmine since that
happened.
2017-02-01 13:36:20 +00:00
Michał Gołębiowski 11f700f7bd docs($location): fix examples
The examples contained tests with assertions in form of regular equality
comparisons which would be noops and in case of an error nothing would get
reported. Also, one of the test mixed a HTML5 browser scenario with a non-HTML5
one.
2017-02-01 13:35:15 +00:00
Michał Gołębiowski 5785f2a991 docs($animation): fix weird spaces around colons 2017-02-01 13:29:28 +00:00
Peter Bacon Darwin 2546c29f81 feat(ngModel): add $overrideModelOptions support
This change allows developers to modify the model options for an `ngModel`
directive programmatically.

Closes #15415
2017-02-01 12:20:41 +00:00
Patrick McElhaney 19ea708c9d docs(guide/component): add replace option
Add `replace` to the table comparing components to directives options. The
`replace` option is deprecated, but it is still documented for directives, so
it is worth pointing it out as a difference between directives and components.

Closes #15658
2017-01-31 19:37:47 +02:00
Frederik Prijck 5cf05d67f2 chore(doc-gen): show arguments as a subsection of the usage section
Previously, on the docs of directives which include the `animation` section, `arguments` are shown as an `h3` element below the `animation` `h2` element, making it look like it's a subsection of `animations`.

This commit ensures that the àrgument` `h3`element is rendered correctly after the `usage` `h2` element.

Fixes #15645
Closes #15646
2017-01-30 19:26:27 +02:00
Georgios Kalpakas 0377c6f0e8 fix($compile): do not swallow thrown errors in test
In e13eeab, errors/rejections produced during fetching the template or compiling
an asynchronous directive, where overzealously silenced. This doesn't make any
difference in (most) production apps, where `$exceptionHandler` does not rethrow
the errors. In tests though (where `$exceptionHandler` rethrows by default), it
can unexpectedly "swallow" thrown errors.

This commit fixes it by removing the extraneous `.catch(noop)`, thus letting
errors thrown by `$exceptionHandler` to surface.

The changes in 'compileSpec.js' essentially revert the modifications that were
unnecessarily (and incorrectly) done in e13eeab (and also one incorrect
modification from [c22615c][1]).

[1]: https://github.com/angular/angular.js/commit/c22615cbfbaa7d1712e79b6bf2ace6eb41313bac#diff-348c2f3781ed66a24894c2046a52c628L2084

Fixes #15629

Closes #15631
2017-01-30 19:25:06 +02:00
Peter Bacon Darwin 9c13866824 chore(docs): don't use bower for docs dependencies 2017-01-30 14:10:25 +00:00
vteremasov 419a4813e3 fix($resource): correctly unescape /\. even if \. comes from a param value
Closes #15627
2017-01-27 18:20:11 +02:00
Martin Staffa 131af8272d fix(select): keep ngModel when selected option is recreated by ngRepeat
Fixes #15630 
Closes #15632
2017-01-27 16:14:18 +01:00
Martin Staffa c219a46f59 docs(ngDisabled): list some elements that natively support
Closes #15473
2017-01-27 16:14:06 +01:00
Jason Bedard 25f008f541 feat($parse): allow watching array/object of inputs to literal values
The inputs of array/object literals are now watched for changes.
If the an input changes then a new instance of the literal will be
provided when the parsed expression is executed.

Closes #15301
2017-01-27 10:06:31 +00:00
frederikprijck 4a593db79b fix($sniffer): allow history for NW.js apps
Previously `$sniffer` incorrectly detected NW.js apps as Chrome Packaged Apps,
disallowing them to use the history API.

This commit correctly detects NW.js apps and allows them to use the History API.

Fixes #15474

Closes #15633
2017-01-27 00:16:54 +02:00
Georgios Kalpakas ad4fef0431 refactor(*): replace HashMap with NgMap
For the time being, we will be using `NgMap`, which is an API-compatible
implementation of native `Map` (for the features required in Angular). This will
make it easy to switch to using the native implementations, once they become
more stable.

Note:
At the moment some native implementations are still buggy (often in subtle ways)
and can cause hard-to-debug failures.)

Closes #15483
2017-01-25 23:57:16 +02:00
Georgios Kalpakas 8a15fcc1f5 test(hashKey): add tests for hashKey() 2017-01-25 23:57:15 +02:00
Georgios Kalpakas f01212ab52 fix(ngAnimate): correctly animate transcluded clones with templateUrl
Previously, `$animate` would decide whether an animation should be cancelled
based on some assumption that didn't hold in specific cases (e.g. when animating
transcluded clones with `templateUrl` directives on them for the first time). As
a result, the entering elements would not be animated in such cases. This
affected commonly used, structural built-in directives (`ngIf`, `ngRepeat`,
`ngSwitch` etc).
This commit fixes it by avoiding invalid assumptions (i.e. by taking into
account the transformations that take place while compiling such elements).

Partly addresses #14074 and #14124.

Fixes #15510

Closes #15514
2017-01-24 23:56:04 +02:00
Georgios Kalpakas 28693a1a67 test(ngAnimate): make expectations more specific 2017-01-24 23:56:04 +02:00
Georgios Kalpakas 29fd499552 refactor(ngAnimate): simplify functions and remove redundant args/calls
Simplifies/Optimizes the following functions:

- `areAnimationsAllowed()`
- `cleanupEventListeners()`
- `closeChildAnimations()`
- `clearElementAnimationState()`
- `markElementAnimationState()`
- `findCallbacks()`

Although not its primary aim, this commit also offers a small performance boost
to animations (~5% as measured with the `animation-bp` benchmark).
2017-01-24 23:56:03 +02:00
Georgios Kalpakas 2f97d9d647 chore(benchmarks): add basic animation benchmark 2017-01-24 23:56:03 +02:00
Georgios Kalpakas 4146b38459 docs(*): document the breaking change in 7ceb5f6 2017-01-20 22:51:21 +02:00
frederikprijck 05aab660ce fix(ngValue): correctly update the value property when value is undefined
Previously, when the expression evaluated to `undefined` the `value` property
was not updated. This happened because jqLite/jQuery's `prop(_, undefined)` is
treated as a getter, thus not apdating the property.

This commit fixes it by setting the property to `null` instead.
(On IE9 we use `''` - see inline comments for more info.)

Fixes #15603

Closes #15605
2017-01-19 14:45:28 +02:00
Peter Bacon Darwin 5ecb64849e chore(package): relax yarn version constraint 2017-01-19 09:26:25 +00:00
Martin Brown 59dfe1b5a0 docs(guide/i18n): fix typos
Closes #15616
2017-01-17 18:18:48 +02:00
Georgios Kalpakas 50ebfb735c docs(changelog): update with changes for 1.5.11 2017-01-13 01:13:18 +02:00
Georgios Kalpakas 0bdbfe5069 style($compile): remove trailing whitespace 2017-01-12 23:12:44 +02:00
Grace Benz 3fc4d6028c docs($compile): add some detail about $onChanges
Closes #15604
2017-01-12 22:42:50 +02:00
Georgios Kalpakas 2eb12a052b test(e2e): make test less flakey-prone 2017-01-12 22:25:47 +02:00
Peter Bacon Darwin bd63b2235c fix(ngMockE2E): ensure that mocked $httpBackend uses correct $browser
The fix from #13124 enabled ngMock and ngMockE2E to work together but
did it in a way that meant that the "real" `$httpBackend` service that
was used in pass-through depended upon a different `$browser` service
to the rest of the app.

This broke Protractor since it watches the `$browser` for outstanding
requests and the pass through requests were being tracked by the wrong
`$browser` instance.

Closes #15593
2017-01-12 10:59:26 +00:00
Georgios Kalpakas f418ffd083 revert: fix($sce): consider document base URL in 'self' URL policy
This reverts commit cce98ff53a.
Reverting while investigating security implications of cce98ff without #15597
(which is possibly a breaking change, thus not suitable for this branch).
2017-01-12 11:24:10 +02:00
Georgios Kalpakas 6ab5f8ce4b chore(*): fix yarn.lock
(It turns out cherry-picking it from master wasn't a great idea :/)
2017-01-12 01:08:55 +02:00
Georgios Kalpakas becfeb5aa3 chore(*): update dgeni-packages (and other devDependencies)
`dgeni-packages` prior to version 0.16.3 specified `engine.yarn: '^0.17.9'`,
which was unnecessarily strict and would cause any task to fail if someone had a
yarn version >=0.18.0.
Other devDependencies were also updated (because why not).

Closes #15600
2017-01-12 01:02:08 +02:00
Sammy Jelin eb968c4a68 fix($route): make asynchronous tasks count as pending requests
Protractor users were having a problem where if they had asynchonous code in a
`route.resolve` or `route.resolveRedirectTo` variable, Protractor was not
waiting for that code to complete before continuing. See
https://github.com/angular/protractor/issues/789#issuecomment-190983200 for
details.

This commit fixes it by ensuring that `$browser#outstandingRequestCount` is
properly increased/decreased while `$route` (asynchronously) processes a route.

Also, enhanced `ngMock` to wait for pending requests, before calling callbacks
from `$browser.notifyWhenNoOutstandingRequests()`.

Related to angular/protractor#789.

Closes #14159
2017-01-12 00:15:20 +02:00
frederikprijck 7f2af3f923 fix($compile): allow the usage of "$" in isolate scope property alias
Previously, when using an alias for an isolate scope or `bindings` property
(e.g. `alias: '<attrName'` instead of `attrName: '<'`), a `$compile:iscp` error
was thrown if the attribute name contained a "$".
This commit removes the error by changing the regex to allow "$" characters in
the attribute name when using a property alias.

Fixes: #15586

Closes #15594
2017-01-11 11:46:31 +02:00
Alex Dobkin cce98ff53a fix($sce): consider document base URL in 'self' URL policy
Page authors can use the `<base>` tag in HTML to specify URL to use as a base
when resovling relative URLs. This can cause SCE to reject relative URLs on the
page, because they fail the same-origin test.

To improve compatibility with the `<base>` tag, this commit changes the logic
for matching URLs to the 'self' policy to allow URLs that match the protocol and
domain of the base URL in addition to URLs that match the loading origin.

**Security Note:**
If an attacker can inject a `<base>` tag into the page, they can circumvent SCE
protections. However, injecting a `<base>` tag typically requires the ability to
inject arbitrary HTML into the page, which is a more serious vulnerabilty than
bypassing SCE.

Fixes #15144

Closes #15145
2017-01-10 14:51:09 +02:00
Georgios Kalpakas b607618342 fix($location): correctly handle external URL change during $digest
Previously, when the URL was changed directly (e.g. via `location.href`) during
a `$digest` (e.g. via `scope.$evalAsync()` or `promise.then()`) the change was
not handled correctly, unless a `popstate` or `hashchange` event was fired
synchronously.

This was an issue when calling `history.pushState()/replaceState()` in all
browsers, since these methods do not emit any event. This was also an issue when
setting `location.href` in IE11, where (unlike other browsers) no `popstate`
event is fired at all for hash-only changes ([known bug][1]) and the
`hashchange` event is fired asynchronously (which is too late).

This commit fixes both usecases by:

1. Keeping track of `$location` setter methods being called and only processing
   a URL change if it originated from such a call. If there is a URL difference
   but no setter method has been called, this means that the browser URL/history
   has been updated directly and the change hasn't yet been propagated to
   `$location` (e.g. due to no event being fired synchronously or at all).
2. Checking for URL/state changes at the end of the `$digest`, in order to
   detect changes via `history` methods (that took place during the `$digest`).

[1]: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/3740423/

Fixes #11075
Fixes #12571
Fixes #15556

Closes #15561
2017-01-10 00:03:10 +02:00
Georgios Kalpakas fa50fbaf57 fix(*): detect external changes in history.state
Previously, `$browser.$$checkUrlChange()` (which was run before each `$digest`)
would only detect an external change (i.e. not via `$location`) to the browser
URL. External changes to `history.state` would not be detected and propagated to
`$location`.

This would not be a problem if changes were followed by a `popstate` or
`hashchange` event (which would call `cacheStateAndFireUrlChange()`). But since
`history.pushState()/replaceState()` do not fire any events, calling these
methods manually would result in `$location` getting out-of-sync with the actual
history state.

This was not detected in tests, because the mocked `window.history` would
incorrectly trigger `popstate` when calling `pushState()/replaceState()`, which
"covered" the bug.

This commit fixes it by always calling `cacheState()`, before looking for and
propagating a URL/state change.
2017-01-10 00:03:09 +02:00
David Jöch f135e2dc05 style($log): fix indentation
Closes #15579
2017-01-05 13:02:59 +02:00
Florian Berger 780351db5e docs(*): fix typos
Closes #15577
2017-01-05 11:10:52 +02:00
Nic Mitchell a50bb0bfec chore(*): update copyright year
Closes #15573
2017-01-04 12:21:08 +02:00
Georgios Kalpakas 4d86df6f48 docs(guide/$location): correctly format heading 2017-01-03 22:37:17 +02:00
Georgios Kalpakas bb464d16b4 fix(angularInit): allow auto-bootstraping from inline script
Some browsers (e.g. Safari 9.x, PhantomJS) do not set `link.origin/protocol`
correctly, when setting `link.href` to `null`, which prevented auto-bootstraping
Angular from scripts without a `src` attribute (i.e. inline scripts).
Inline scripts are on the same origin as the loading page, so auto-bootstraping
should be allowed.

Fixes #15567

Closes #15571
2017-01-03 19:23:43 +02:00
Soumya Ranjan Mohanty 85b2eb1472 docs(guide/services): fix syntax for Jasmine v2.x
Closes #15570
2017-01-03 16:14:32 +02:00
Georgios Kalpakas 7608f92c6a docs(ngShow/ngHide): improve docs and fix inconsistencies between ngShow/ngHide
Closes #15471
2017-01-03 12:55:38 +02:00
Deco c0bf8db63c docs(tutorial/step_04): fix typo
Closes #15562
2016-12-31 12:32:22 +02:00
Georgios Kalpakas c95a6737fb fix(input): fix step validation for input[type=number/range]
Previously, the validation would incorrectly fail in certain cases (e.g.
`step: 0.01`, `value: 1.16 or 20.1`), due to Floating Point Arithmetic
limitations. The previous fix for FPA limitations (081d06ff) tried to solve the
issue by converting the numbers to integers, before doing the actual
calculation, but it failed to account for cases where the conversion itself
returned non-integer values (again due to FPA limitations).
This commit fixes it by ensuring that the values used in the final calculation
are always integers.

Fixes #15504

Closes #15506
2016-12-29 10:18:32 +02:00
Georgios Kalpakas cd43d24402 docs(CHANGELOG.md): add missing commas 2016-12-29 10:09:04 +02:00
supasak 086c5d0354 fix($resource): delete $cancelRequest() in toJSON()
Closes #15244
2016-12-29 10:01:11 +02:00
Peter Bacon Darwin 2a2ac5f53a docs(CHANGELOG): add 1.6.1 release info 2016-12-23 10:38:58 +00:00
Peter Bacon Darwin 21deaf637a chore(deps): update changez-angular version 2016-12-23 10:38:48 +00:00
Naomi Black 72e15a3a83 docs(guide/forms): remove implicit bias from example
Closes #15543
2016-12-23 11:43:47 +02:00
Thomas Grainger 174cb4a8c8 fix($q): Add traceback to unhandled promise rejections
Fixes #14631
Closes #15527
2016-12-21 20:12:29 +01:00
sp00m 33f769b0a1 fix($$cookieReader): correctly handle forbidden access to document.cookie
In certain cases (e.g. on LG webOS using the `file:` protocol), access to
`document.cookie` may not be allowed and throw an error. This could break
`$http` which relies on `$$cookieReader()` for retrieving the XSRF token.
This commit fixes it by treating `document.cookie` as empty, when access to it
is fordibben.

Fixes  #15523

Closes #15532
2016-12-20 23:34:07 +02:00
Simon Legner c8abf20558 docs(CHANGELOG): fix typo
Closes #15529
2016-12-20 10:33:14 +02:00
Thomas Grainger 6dbb183e75 docs(guide/migration): improve grammar
Closes #15526
2016-12-20 01:17:19 +02:00
Georgios Kalpakas bc4844d3b2 fix(ngOptions): do not unset the selected property unless necessary
Previously, when updating the value of a `select[multiple]` element, all options
were first set to `selected = false` and then the selected ones were set to
`true`. By setting an already selected option to `selected = false` and then
`true` again - essentially unselecting and reselecting it - caused some browsers
(including Firefox, IE and under some circumstances Chrome) to unexpectedly
scroll to the last selected option.

This commit fixes it by ensuring that the `selected` property is only set if its
current value is different than the new one and even then it is set to its final
value at once (i.e. without first setting it to `false`), thus avoiding the
undesirable behavior.

Fixes #15477

Closes #15478
2016-12-19 22:52:18 +02:00
Peter Neave 708f8b47de docs(tutorial/step_13): add missing dependency phoneDetails module
Closes #15521
2016-12-19 21:09:21 +02:00
Georgios Kalpakas 5518126d42 docs(CHANGELOG.md): remove reverted bug fix
Related to 02f045b.
2016-12-16 11:16:08 +02:00
Peter Bacon Darwin 5fe73fdc3a docs(CHANGELOG): add note to 1.5.0-beta.1 2016-12-16 11:16:08 +02:00
Georgios Kalpakas 394c496bf2 docs(CHANGELOG.md): add changes for v1.5.10 2016-12-15 19:29:37 +02:00
Georgios Kalpakas 8e1aeba715 docs(CHANGELOG.md): add missing entries for v1.6.0-rc.0/v1.6.0 2016-12-15 19:29:06 +02:00
Georgios Kalpakas 0e6e7eb477 docs($q): document the default value for errorOnUnhandledRejections 2016-12-14 00:44:36 +02:00
Jannick Fahlbusch 183f636816 docs($interval): improve fn description
If no additional arguments are passed, the function is called with the current iteration count.

Closes #15503
2016-12-13 14:16:20 +02:00
Georgios Kalpakas 5f8ed63f2a fix(ngModelOptions): work correctly when on the template of replace directives
Previously, in order for `ngModel` and `ngModelOptions` to work correctly
together, the latter's pre-linking function should be run before the former's
pre-linking function. This was typically what happened, except when `ngModel`
was used on an element which also had a `replace` directive, whose template
included `ngModelOptions`. In that case, the order was reversed.

This commit fixes it by moving the initialization logic of `ngModelOptions` from
its pre-linking function to its controller's `$onInit()` lifecycle hook.

Fixes #15492

Closes #15493
2016-12-13 00:12:34 +02:00
Georgios Kalpakas e4f3c94e31 refactor(testabilityPatch): remove code duplication 2016-12-13 00:11:18 +02:00
Aaron Brewer 1e5cbcbd93 docs(ngMessageExp): improve description
Closes #15486
2016-12-11 21:01:57 +02:00
idhindsight dcf3ec160f docs(tutorial/step_09): fix typo (it's --> its)
Closes #15485
2016-12-10 22:27:59 +02:00
David Rodenas Pico a7beb5b6d3 chore(benchpress): add an ngClass benchmark
Closes #15243
2016-12-09 12:21:21 +02:00
Georgios Kalpakas 1d3b65adc2 perf(ngClass): avoid unnecessary .data() accesses, deep-watching and copies
Includes the following commits (see #15246 for details):

- **perf(ngClass): only access the element's `data` once**

- **refactor(ngClass): simplify conditions**

- **refactor(ngClass): move helper functions outside the closure**

- **refactor(ngClass): exit `arrayDifference()` early if an input is empty**

- **perf(ngClass): avoid deep-watching (if possible) and unnecessary copies**

  The cases that should benefit most are:

  1. When using large objects as values (e.g.: `{loaded: $ctrl.data}`).
  2. When using objects/arrays and there are frequent changes.
  3. When there are many `$index` changes (e.g. addition/deletion/reordering in large `ngRepeat`s).

  The differences in operations per digest include:

  1. `Regular expression (when not changed)`
     **Before:** `equals()`
     **After:**  `toClassString()`

  2. `Regular expression (when changed)`
     **Before:** `copy()` + 2 x `arrayClasses()` + `shallowCopy()`
     **After:**  2 x `split()`

  3. `One-time expression (when not changed)`
     **Before:** `equals()`
     **After:**  `toFlatValue()` + `equals()`*

  4. `One-time expression (when changed)`
     **Before:** `copy()` + 2 x `arrayClasses()` + `shallowCopy()`
     **After:**  `copy()`* + `toClassString()`* + 2 x `split()`

  5. `$index modulo changed`
     **Before:** `arrayClasses()`
     **After:**  -

  (*): on flatter structure

  In large based on #14404. Kudos to @drpicox for the initial idea and a big part
  of the implementation.

Closes #14404

Closes #15246
2016-12-09 12:04:47 +02:00
Georgios Kalpakas d528644fe3 fix(ngClassOdd/Even): add/remove the correct classes when expression/$index change simultaneously 2016-12-09 12:03:38 +02:00
David Rodenas Pico 6f1bcfc14e test(ngClass): add some tests about one-time bindings and objects inside arrays 2016-12-09 12:01:37 +02:00
Georgios Kalpakas 996914c6b0 refactor(ngClass): remove redundant $observer and dependency on $animate
Includes the following commits (see #15246 for details):

- **refactor(ngClass): remove unnecessary dependency on `$animate`**

- **refactor(ngClass): remove redundant `$observe`r**

  The code was added in b41fe9f in order to support having both `ngClass` and
  interpolation in `class` work together. `ngClass` has changed considerably since
  b41fe9f and for simple cases to work the `$observe`r is no longer necessary (as
  indicated by the expanded test still passing).

  That said, it is a [documented known issue][1] that `ngClass` should not be used
  together with interpolation in `class` and more complicated cases do not work
  anyway.

[1]: https://docs.angularjs.org/api/ng/directive/ngClass#known-issues
2016-12-09 12:00:41 +02:00
Aman Mittal fff048d099 docs(guide/external-resources): add "AngularJS in Action" book
Closes #15480
2016-12-09 11:36:26 +02:00
Peter Bacon Darwin dcb0da8225 chore(docs): fix plnkrOpener to use $onInit
Since 1.6.0 does not preassign bindings before running the controller
constructor function, we must move initialisation into the `$onInit`
method.
2016-12-09 11:28:23 +02:00
Peter Bacon Darwin b664e20d12 chore(package): update docs app to run on 1.6.0 2016-12-09 11:27:20 +02:00
Georgios Kalpakas 3d68b95028 fix(jqLite): silently ignore after() if element has no parent
Previously, the element was always assumed to have a parent and an error was
thrown when that was not the case.
This commit makes jqLite consistent with jQuery, which silently ignores a call
on elements that do not have a parent.

Fixes #15331
Closes #15367

Closes #15475
2016-12-09 10:56:14 +02:00
Georgios Kalpakas 163aca336d fix($rootScope): when adding/removing watchers during $digest
Previously, adding a watcher during a `$digest` (i.e. from within a watcher),
would result in the next watcher getting skipped. Similarly, removing a watcher
during a `$digest` could result in the current watcher being run twice (if the
removed watcher had not run yet in the current `$digest`).

This commit fixes both cases by keeping track of the current watcher index
during a digest and properly updating it when adding/removing watchers.

Fixes #15422

Closes #15424
2016-12-09 10:44:24 +02:00
130 changed files with 5592 additions and 2421 deletions
+495 -26
View File
@@ -1,3 +1,287 @@
<a name="1.6.3"></a>
# 1.6.3 scriptalicious-bootstrapping (2017-03-08)
## Bug Fixes
- **Angular:**
- do not auto-bootstrap if the `src` exists but is empty
([3536e8](https://github.com/angular/angular.js/commit/3536e83d8a085b02bd6dcec8324800b7e6c734e4))
- do not auto bootstrap if the currentScript has been clobbered
([95f964](https://github.com/angular/angular.js/commit/95f964b827b6f5b5aab10af54f7831316c7a9935))
- do not auto-bootstrap if the script source is bad and inside SVG
([c8f78a](https://github.com/angular/angular.js/commit/c8f78a8ca9debc33a6deaf951f344b8d372bf210))
- **$log:** don't parse error stacks manually outside of IE/Edge
([64e5af](https://github.com/angular/angular.js/commit/64e5afc4786fdfd850c6bdb488a5aa2b8b077f74),
[#15590](https://github.com/angular/angular.js/issues/15590),
[#15767](https://github.com/angular/angular.js/issues/15767))
- **$sanitize:** prevent clobbered elements from freezing the browser
([3bb1dd](https://github.com/angular/angular.js/commit/3bb1dd5d7f7dcde6fea5a3148f8f10e92f451e9d),
[#15699](https://github.com/angular/angular.js/issues/15699))
- **$animate:**
- reset `classNameFilter` to `null` when a disallowed RegExp is used
([a584fb](https://github.com/angular/angular.js/commit/a584fb6e1569fc1dd85e23b251a7c126edc2dd5b),
[#14913](https://github.com/angular/angular.js/issues/14913))
- improve detection on `ng-animate` in `classNameFilter` RegExp
([1f1331](https://github.com/angular/angular.js/commit/1f13313f403381581e1c31c57ebfe7a96546c6e4),
[#14806](https://github.com/angular/angular.js/issues/14806))
- **filterFilter:** don't throw if `key.charAt` is not a function
([f27d19](https://github.com/angular/angular.js/commit/f27d19ed606bf05ba41698159ebbc5fbc195033e),
[#15644](https://github.com/angular/angular.js/issues/15644),
[#15660](https://github.com/angular/angular.js/issues/15660))
- **select:**
- add attribute "selected" for `select[multiple]`
([851367](https://github.com/angular/angular.js/commit/8513674911300b27d518383a905fde9b3f25f7ae))
- keep original selection when using shift to add options in IE/Edge
([97b74a](https://github.com/angular/angular.js/commit/97b74ad6fbcbc4b63e37e9eb44962d6f8de83e8b),
[#15675](https://github.com/angular/angular.js/issues/15675),
[#15676](https://github.com/angular/angular.js/issues/15676))
- **$jsonpCallbacks:** allow `$window` to be mocked in unit tests
([5ca0de](https://github.com/angular/angular.js/commit/5ca0de64873c32ab2f540a3226e73c4175a15c50),
[#15685](https://github.com/angular/angular.js/issues/15685),
[#15686](https://github.com/angular/angular.js/issues/15686))
## New Features
- **info:** add `angularVersion` info to each module
([1e582e](https://github.com/angular/angular.js/commit/1e582e4fa486f340150bba95927f1b26d9142de2))
- **$injector:** add new `modules` property
([742123](https://github.com/angular/angular.js/commit/7421235f247e5b7113345401bc5727cfbf81ddc2))
- **Module:** add `info()` method
([09ba69](https://github.com/angular/angular.js/commit/09ba69078de6ba52c70571b82b6205929f6facc5),
[#15225](https://github.com/angular/angular.js/issues/15225))
- **errorHandlingConfig:** make the depth for object stringification in errors configurable
([4a5eaf](https://github.com/angular/angular.js/commit/4a5eaf7bec85ceca8b934ebaff4d1834a1a09f57),
[#15402](https://github.com/angular/angular.js/issues/15402),
[#15433](https://github.com/angular/angular.js/issues/15433))
<a name="1.6.2"></a>
# 1.6.2 llamacorn-lovehug (2017-02-07)
## Bug Fixes
- **$compile:**
- do not swallow thrown errors in testsg
([0377c6](https://github.com/angular/angular.js/commit/0377c6f0e890cb4ed3eb020b96720b4b34f75df3),
[#15629](https://github.com/angular/angular.js/issues/15629),
[#15631](https://github.com/angular/angular.js/issues/15631))
- allow the usage of "$" in isolate scope property alias
([7f2af3](https://github.com/angular/angular.js/commit/7f2af3f923e7a3f85c8862d0ed57d21c72eae904),
[#15594](https://github.com/angular/angular.js/issues/15594))
- **$location:** correctly handle external URL change during `$digest`
([b60761](https://github.com/angular/angular.js/commit/b607618342d6c4fab364966fe05f152be6bd4d5f),
[#11075](https://github.com/angular/angular.js/issues/11075),
[#12571](https://github.com/angular/angular.js/issues/12571),
[#15556](https://github.com/angular/angular.js/issues/15556),
[#15561](https://github.com/angular/angular.js/issues/15561))
- **$browser:** detect external changes in `history.state`
([fa50fb](https://github.com/angular/angular.js/commit/fa50fbaf57b3437be7a410ecaba7008dbe0ef239))
- **$resource:**
- do not swallow errors in `success` callback
([27146e](https://github.com/angular/angular.js/commit/27146e8a7fad54c1342179b6d291b1b5c2ebe816),
[#15624](https://github.com/angular/angular.js/issues/15624),
[#15628](https://github.com/angular/angular.js/issues/15628))
- correctly unescape `/\.` even if `\.` comes from a param value
([419a48](https://github.com/angular/angular.js/commit/419a4813e354496bdf0df44e3f8afaa198df1ab1),
[#15627](https://github.com/angular/angular.js/issues/15627))
- delete `$cancelRequest()` in `toJSON()`
([086c5d](https://github.com/angular/angular.js/commit/086c5d0354db8cb3d106b9ff966fb48d6fb46ef8),
[#15244](https://github.com/angular/angular.js/issues/15244))
- **$animate:** correctly animate transcluded clones with `templateUrl`
([f01212](https://github.com/angular/angular.js/commit/f01212ab5287ac7a154da7d75037ed444e81eb34),
[#15510](https://github.com/angular/angular.js/issues/15510),
[#15514](https://github.com/angular/angular.js/issues/15514))
- **$route:** make asynchronous tasks count as pending requests
([eb968c](https://github.com/angular/angular.js/commit/eb968c4a6884838db05369a04459066424c5bba8),
[#14159](https://github.com/angular/angular.js/issues/14159))
- **$parse:** make sure ES6 object computed properties are watched
([5e418b](https://github.com/angular/angular.js/commit/5e418b1145a1045da598c7863e785d647ea83850),
[#15678](https://github.com/angular/angular.js/issues/15678))
- **$sniffer:** allow `history` for NW.js apps
([4a593d](https://github.com/angular/angular.js/commit/4a593db79ba1e21a6aa600a82cf6d757cad94d01),
[#15474](https://github.com/angular/angular.js/issues/15474),
[#15633](https://github.com/angular/angular.js/issues/15633))
- **input:** fix `step` validation for `input[type=number/range]`
([c95a67](https://github.com/angular/angular.js/commit/c95a6737fbd277e40c064bd9f68f383bf119505c),
[#15504](https://github.com/angular/angular.js/issues/15504),
[#15506](https://github.com/angular/angular.js/issues/15506))
- **select:** keep `ngModel` when selected option is recreated by `ngRepeat`
([131af8](https://github.com/angular/angular.js/commit/131af8272d269a541d04cb522c264a91e0ec8b6a),
[#15630](https://github.com/angular/angular.js/issues/15630),
[#15632](https://github.com/angular/angular.js/issues/15632))
- **ngValue:** correctly update the `value` property when `value` is undefined
([05aab6](https://github.com/angular/angular.js/commit/05aab660ce74f526f2110d3b5faf9a5b4f4e664b)
[#15603](https://github.com/angular/angular.js/issues/15603),
[#15605](https://github.com/angular/angular.js/issues/15605))
- **angularInit:** allow auto-bootstrapping from inline script
([bb464d](https://github.com/angular/angular.js/commit/bb464d16b434b9e2de2fecf80c192d4741cba879),
[#15567](https://github.com/angular/angular.js/issues/15567),
[#15571](https://github.com/angular/angular.js/issues/15571))
- **ngMockE2E:** ensure that mocked `$httpBackend` uses correct `$browser`
([bd63b2](https://github.com/angular/angular.js/commit/bd63b2235cd410251cb83eebd9a47d3102830b6b),
[#15593](https://github.com/angular/angular.js/issues/15593))
## New Features
- **ngModel:** add `$overrideModelOptions` support
([2546c2](https://github.com/angular/angular.js/commit/2546c29f811b68eea4d68be7fa1c8f7bb562dc11),
[#15415](https://github.com/angular/angular.js/issues/15415))
- **$parse:** allow watching array/object literals with non-primitive values
([25f008](https://github.com/angular/angular.js/commit/25f008f541d68b09efd7b428b648c6d4899e6972),
[#15301](https://github.com/angular/angular.js/issues/15301))
<a name="1.5.11"></a>
# 1.5.11 princely-quest (2017-01-13)
## Bug Fixes
- **$compile:** allow the usage of "$" in isolate scope property alias
([e75fbc](https://github.com/angular/angular.js/commit/e75fbc494e6a0da6a9231b40bb0382431b62be07),
[#15586](https://github.com/angular/angular.js/issues/15586),
[#15594](https://github.com/angular/angular.js/issues/15594))
- **angularInit:** allow auto-bootstraping from inline script
([41aa91](https://github.com/angular/angular.js/commit/41aa9125b9aaf771addb250642f524a4e6f9d8d3),
[#15567](https://github.com/angular/angular.js/issues/15567),
[#15571](https://github.com/angular/angular.js/issues/15571))
- **$resource:** delete `$cancelRequest()` in `toJSON()`
([4f3858](https://github.com/angular/angular.js/commit/4f3858e7c371f87534397f45b9d002add33b00cc),
[#15244](https://github.com/angular/angular.js/issues/15244))
- **$$cookieReader:** correctly handle forbidden access to `document.cookie`
([6933cf](https://github.com/angular/angular.js/commit/6933cf64fe51f54b10d1639f2b95bab3c1178df9),
[#15523](https://github.com/angular/angular.js/issues/15523),
[#15532](https://github.com/angular/angular.js/issues/15532))
<a name="1.6.1"></a>
# 1.6.1 promise-rectification (2016-12-23)
## Bug Fixes
- **$q:** Add traceback to unhandled promise rejections
([174cb4](https://github.com/angular/angular.js/commit/174cb4a8c81e25581da5b452c2bb43b0fa377a9b),
[#14631](https://github.com/angular/angular.js/issues/14631))
- **$$cookieReader:** correctly handle forbidden access to `document.cookie`
([33f769](https://github.com/angular/angular.js/commit/33f769b0a1214055c16fb59adad4897bf53d62bf),
[#15523](https://github.com/angular/angular.js/issues/15523))
- **ngOptions:** do not unset the `selected` property unless necessary
([bc4844](https://github.com/angular/angular.js/commit/bc4844d3b297d80aecef89aa1b32615024decedc),
[#15477](https://github.com/angular/angular.js/issues/15477))
- **ngModelOptions:** work correctly when on the template of `replace` directives
([5f8ed6](https://github.com/angular/angular.js/commit/5f8ed63f2ab02ffb9c21bf9c29d27c851d162e26),
[#15492](https://github.com/angular/angular.js/issues/15492))
- **ngClassOdd/Even:** add/remove the correct classes when expression/`$index` change simultaneously
([d52864](https://github.com/angular/angular.js/commit/d528644fe3e9ffd43999e7fc67806059f9e1083e))
- **jqLite:** silently ignore `after()` if element has no parent
([3d68b9](https://github.com/angular/angular.js/commit/3d68b9502848ff6714ef89bfb95b8e70ae34eff6),
[#15331](https://github.com/angular/angular.js/issues/15331),
[#15475](https://github.com/angular/angular.js/issues/15475))
- **$rootScope:** when adding/removing watchers during $digest
([163aca](https://github.com/angular/angular.js/commit/163aca336d7586a45255787af41b14b2a12361dd),
[#15422](https://github.com/angular/angular.js/issues/15422))
## Performance Improvements
- **ngClass:** avoid unnecessary `.data()` accesses, deep-watching and copies
([1d3b65](https://github.com/angular/angular.js/commit/1d3b65adc2c22ff662159ef910089cf10d1edb7b),
[#14404](https://github.com/angular/angular.js/issues/14404))
<a name="1.5.10"></a>
# 1.5.10 asynchronous-synchronization (2016-12-15)
## Bug Fixes
- **$compile:**
- don't throw tplrt error when there is whitespace around a top-level comment
([12752f](https://github.com/angular/angular.js/commit/12752f66ac425ab38a5ee574a4bfbf3516adc42c),
[#15108](https://github.com/angular/angular.js/issues/15108))
- clean up `@`-binding observers when re-assigning bindings
([f3cb6e](https://github.com/angular/angular.js/commit/f3cb6e309aa1f676e5951ac745fa886d3581c2f4),
[#15268](https://github.com/angular/angular.js/issues/15268))
- set attribute value even if `ngAttr*` contains no interpolation
([229799](https://github.com/angular/angular.js/commit/22979904fb754c59e9f6ee5d8763e3b8de0e18c2),
[#15133](https://github.com/angular/angular.js/issues/15133))
- `bindToController` should work without `controllerAs`
([944989](https://github.com/angular/angular.js/commit/9449893763a4fd95ee8ff78b53c6966a874ec9ae),
[#15088](https://github.com/angular/angular.js/issues/15088))
- do not overwrite values set in `$onInit()` for `<`-bound literals
([07e1ba](https://github.com/angular/angular.js/commit/07e1ba365fb5e8a049be732bd7b62f71e0aa1672),
[#15118](https://github.com/angular/angular.js/issues/15118))
- avoid calling `$onChanges()` twice for `NaN` initial values
([0cf5be](https://github.com/angular/angular.js/commit/0cf5be52642f7e9d81a708b3005042eac6492572))
- **$location:** prevent infinite digest with IDN urls in Edge
([4bf892](https://github.com/angular/angular.js/commit/4bf89218130d434771089fdfe643490b8d2ee259),
[#15217](https://github.com/angular/angular.js/issues/15217))
- **$rootScope:** correctly handle adding/removing watchers during `$digest`
([a9708d](https://github.com/angular/angular.js/commit/a9708de84b50f06eacda33834d5bbdfc97c97f37),
[#15422](https://github.com/angular/angular.js/issues/15422))
- **$sce:** fix `adjustMatcher` to replace multiple `*` and `**`
([78eecb](https://github.com/angular/angular.js/commit/78eecb43dbb0500358d333aea8955bd0646a7790))
- **jqLite:** silently ignore `after()` if element has no parent
([77ed85](https://github.com/angular/angular.js/commit/77ed85bcd3be057a5a79231565ac7accc6d644c6),
[#15331](https://github.com/angular/angular.js/issues/15331))
- **input[radio]:** use non-strict comparison for checkedness
([593a50](https://github.com/angular/angular.js/commit/593a5034841b3b7661d3bcbdd06b7a9d0876fd34))
- **select, ngOptions:**
- let `ngValue` take precedence over option text with multiple interpolations
([5b7ec8](https://github.com/angular/angular.js/commit/5b7ec8c84e88ee08aacaf9404853eda0016093f5),
[#15413](https://github.com/angular/angular.js/issues/15413))
- don't add comment nodes as empty options
([1d29c9](https://github.com/angular/angular.js/commit/1d29c91c3429de96e4103533752700d1266741be),
[#15454](https://github.com/angular/angular.js/issues/15454))
- **ngClassOdd/Even:** add/remove the correct classes when expression/`$index` change simultaneously
([e3d020](https://github.com/angular/angular.js/commit/e3d02070ab8a02c818dcc5114db6fba9d3f385d6))
- **$sanitize:** reduce stack height in IE <= 11
([862dc2](https://github.com/angular/angular.js/commit/862dc2532f8126a4a71fd3d957884ba6f11f591c),
[#14928](https://github.com/angular/angular.js/issues/14928))
- **ngMock/$controller:** respect `$compileProvider.preAssignBindingsEnabled()`
([75c83f](https://github.com/angular/angular.js/commit/75c83ff3195931859a099f7a95bf81d32abf2eb3))
## New Features
- **bootstrap:** do not bootstrap from unknown schemes with a different origin
([bdeb33](https://github.com/angular/angular.js/commit/bdeb3392a8719131ab2b993f2a881c43a2860f92),
[#15428](https://github.com/angular/angular.js/issues/15428))
- **$anchorScroll:** convert numeric hash targets to string
([a52640](https://github.com/angular/angular.js/commit/a5264090b66ad0cf9a93de84bb7b307868c0edef),
[#14680](https://github.com/angular/angular.js/issues/14680))
- **$compile:**
- add `preAssignBindingsEnabled` option
([f86576](https://github.com/angular/angular.js/commit/f86576def44005f180a66e3aa12d6cc73c1ac72c))
- throw error when directive name or factory function is invalid
([5c9399](https://github.com/angular/angular.js/commit/5c9399d18ae5cd79e6cf6fc4377d66df00f6fcc7),
[#15056](https://github.com/angular/angular.js/issues/15056))
- **$controller:** throw when requested controller is not registered
([9ae793](https://github.com/angular/angular.js/commit/9ae793d8a69afe84370b601e07fc375fc18a576a),
[#14980](https://github.com/angular/angular.js/issues/14980))
- **$location:** add support for selectively rewriting links based on attribute
([a4a222](https://github.com/angular/angular.js/commit/a4a22266f127d3b9a6818e6f4754f048e253f693))
- **$resource:** pass `status`/`statusText` to success callbacks
([a8da25](https://github.com/angular/angular.js/commit/a8da25c74d2c1f6265f0fafd95bf72c981d9d678),
[#8341](https://github.com/angular/angular.js/issues/8841),
[#8841](https://github.com/angular/angular.js/issues/8841))
- **ngSwitch:** allow multiple case matches via optional attribute `ngSwitchWhenSeparator`
([0e1651](https://github.com/angular/angular.js/commit/0e1651bfd28ba73ebd0e4943d85af48c4506e02c),
[#3410](https://github.com/angular/angular.js/issues/3410),
[#3516](https://github.com/angular/angular.js/issues/3516))
## Performance Improvements
- **all:** don't trigger digests after enter/leave of structural directives
([c57779](https://github.com/angular/angular.js/commit/c57779d8725493c5853dceda0105dafd5c0e3a7c),
[#15322](https://github.com/angular/angular.js/issues/15322))
- **$compile:** validate `directive.restrict` property on directive init
([31d464](https://github.com/angular/angular.js/commit/31d464feef38b1cc950da6c8dccd0f194ebfc68b))
- **ngOptions:** avoid calls to `element.value`
([e269ad](https://github.com/angular/angular.js/commit/e269ad1244bc50fee9218f7c18fab3e9ab063aab))
- **jqLite:** move bind/unbind definitions out of the loop
([7717b9](https://github.com/angular/angular.js/commit/7717b96e950a5916a5f12fd611c73d3b06a8d717))
<a name="1.6.0"></a>
# 1.6.0 rainbow-tsunami (2016-12-08)
@@ -8,14 +292,23 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
- **ngModelOptions:** allow options to be inherited from ancestor `ngModelOptions`
([296cfc](https://github.com/angular/angular.js/commit/296cfce40c25e9438bfa46a0eb27240707a10ffa),
[#10922](https://github.com/angular/angular.js/issues/10922))
- **$compile:** set `preAssignBindingsEnabled` to false by default
([bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858),
[#15352](https://github.com/angular/angular.js/issues/15352))
- **$compile:**
- add `preAssignBindingsEnabled` option
([dfb8cf](https://github.com/angular/angular.js/commit/dfb8cf6402678206132e5bc603764d21e0f986ef))
- set `preAssignBindingsEnabled` to false by default
([bcd0d4](https://github.com/angular/angular.js/commit/bcd0d4d896d0dfdd988ff4f849c1d40366125858),
[#15352](https://github.com/angular/angular.js/issues/15352))
- throw error when directive name or factory function is invalid
([53a3bf](https://github.com/angular/angular.js/commit/53a3bf6634600c3aeff092eacc35edf399b27aec)
[#15056](https://github.com/angular/angular.js/issues/15056))
- **jqLite:**
- implement `jqLite(f)` as an alias to `jqLite(document).ready(f)`
([369fb7](https://github.com/angular/angular.js/commit/369fb7f4f73664bcdab0350701552d8bef6f605e))
- don't throw for elements with missing `getAttribute`
([4e6c14](https://github.com/angular/angular.js/commit/4e6c14dcae4a9a30b3610a288ef8d20db47c4417))
- don't get/set properties when getting/setting boolean attributes
([7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304),
[#14126](https://github.com/angular/angular.js/issues/14126))
- don't remove a boolean attribute for `.attr(attrName, '')`
([3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a))
- remove the attribute for `.attr(attribute, null)`
@@ -38,6 +331,9 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
- JSONP requests now require a trusted resource URL
([6476af](https://github.com/angular/angular.js/commit/6476af83cd0418c84e034a955b12a842794385c4),
[#11352](https://github.com/angular/angular.js/issues/11352))
- **$anchorScroll:** convert numeric hash targets to string
([9062ba](https://github.com/angular/angular.js/commit/9062bae05c002934fe7bfd76043dcc3de9acfde6)
[#14680](https://github.com/angular/angular.js/issues/14680))
- **select:** support values of any type added with `ngValue`
([f02b70](https://github.com/angular/angular.js/commit/f02b707b5e4a5ffd1e1a20d910754cfabfc19622),
[#9842](https://github.com/angular/angular.js/issues/9842))
@@ -51,6 +347,10 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
[#10597](https://github.com/angular/angular.js/issues/10597))
- allow `ngTrim` to work for `input[type=radio]`
([47724b](https://github.com/angular/angular.js/commit/47724baffe050269385b3481e9a9cf4ab3944b4b))
- **ngSwitch:** allow multiple case matches via optional attribute `ngSwitchWhenSeparator`
([0b221](https://github.com/angular/angular.js/commit/0b22173000596bf4b78f6a90083b994d46164d79)
[#3410](https://github.com/angular/angular.js/issues/3410)
[#3516](https://github.com/angular/angular.js/issues/3516))
- **$interpolate:** use custom `toString()` function if present
([a5fd2e](https://github.com/angular/angular.js/commit/a5fd2e4c0376676fa317e09a8d8be4966b82cbfe),
[#7317](https://github.com/angular/angular.js/issues/7317),
@@ -66,18 +366,30 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
([c9dffd](https://github.com/angular/angular.js/commit/c9dffde1cb167660120753181cb6d01dc1d1b3d0),
[#13653](https://github.com/angular/angular.js/issues/13653),
[#7992](https://github.com/angular/angular.js/issues/7992))
- **$location:** default hashPrefix to `'!'`
([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52),
[#13812](https://github.com/angular/angular.js/issues/13812))
- **$resource:** pass `status`/`statusText` to success callbacks
([e3a378](https://github.com/angular/angular.js/commit/e3a378e7a329f60f6b48517f83a4f4c9efecb056)
[#8341](https://github.com/angular/angular.js/issues/8841)
[#8841](https://github.com/angular/angular.js/issues/8841))
- **$location:**
- default hashPrefix to `'!'`
([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52)
[#13812](https://github.com/angular/angular.js/issues/13812))
- add support for selectively rewriting links based on attribute
([3d686a](https://github.com/angular/angular.js/commit/3d686a988dc4373da094cff6905e5b0d8da6afa4))
- **$controller:** throw when requested controller is not registered
([eacfe4](https://github.com/angular/angular.js/commit/eacfe4148eb97e550117ed7fd3c37b58537a9f64)
[#14980](https://github.com/angular/angular.js/issues/14980))
## Security Related
- Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/09/angular-16-expression-sandbox-removal.html).
- **bootstrap:** explicitly whitelist URL schemes for bootstrap. (#15427)
([7f1b8b](https://github.com/angular/angular.js/commit/7f1b8bdfe1043871c5ead2ec602efc41e0de5e53))
- **bootstrap:**
- explicitly whitelist URL schemes for bootstrap.
([7f1b8b](https://github.com/angular/angular.js/commit/7f1b8bdfe1043871c5ead2ec602efc41e0de5e53))
- do not bootstrap from unknown schemes with a different origin
([465d17](https://github.com/angular/angular.js/commit/465d1734559ca4a7f4aa24387060f88fcc53ecb1))
([465d17](https://github.com/angular/angular.js/commit/465d1734559ca4a7f4aa24387060f88fcc53ecb1)
[#15428](https://github.com/angular/angular.js/issues/15428))
- **$compile:**
- secure `link[href]` as a `RESOURCE_URL`s in `$sce`
([04cad4](https://github.com/angular/angular.js/commit/04cad41d26ebaf44b5ee0c29a152d61f235f3efa),
@@ -87,14 +399,18 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
## Bug Fixes
- **$sce:** fix `adjustMatcher` to replace multiple '*' and '**' (#7897)
- **$sce:** fix `adjustMatcher` to replace multiple `*` and `**`
([991a2b](https://github.com/angular/angular.js/commit/991a2b30e00aed1d312e29555e356a795f9e3d62))
- **ngModelOptions:** handle debounce of `updateOn` triggers that are not in debounce list
([789790](https://github.com/angular/angular.js/commit/789790feee4d6c5b1f5d5b18ecb0ccf6edd36fb3))
- **ngMock/$controller:** respect `$compileProvider.preAssignBindingsEnabled()`
([7d9a79](https://github.com/angular/angular.js/commit/7d9a791c6a8c80d29d6c84afa287c81f2a307439))
- **$location:** throw if the path starts with double (back)slashes
([4aa953](https://github.com/angular/angular.js/commit/4aa9534b0fea732d6492a2863c3ee7e077c8d004))
- **$location:**
- prevent infinite digest with IDN URLs in Edge
([705afc](https://github.com/angular/angular.js/commit/705afcd160c8428133b36f2cd63db305dc52f2d7)
[#15217](https://github.com/angular/angular.js/issues/15217))
- throw if the path starts with double (back)slashes
([4aa953](https://github.com/angular/angular.js/commit/4aa9534b0fea732d6492a2863c3ee7e077c8d004))
- **core:** do not auto-bootstrap when loaded from an extension.
([0ff10e](https://github.com/angular/angular.js/commit/0ff10e1b56c6b7c4ac465e35c96a5886e294bac5))
- **input[radio]:** use strict comparison when evaluating checked-ness
@@ -124,6 +440,20 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
- don't throw tplrt error when there is a whitespace around a top-level comment
([76d3da](https://github.com/angular/angular.js/commit/76d3dafdeaf2f343d094b5a34ffb74adf64bb284),
[#15108](https://github.com/angular/angular.js/issues/15108))
- clean up `@`-binding observers when re-assigning bindings
([586e2a](https://github.com/angular/angular.js/commit/586e2acb269016a0fee66ac33f4a385f631afad0)
[#15268](https://github.com/angular/angular.js/issues/15268))
- set attribute value even if `ngAttr*` contains no interpolation
([3fe3da](https://github.com/angular/angular.js/commit/3fe3da8794571a1479d884be26a621f06cdb7842)
[#15133](https://github.com/angular/angular.js/issues/15133))
- `bindToController` should work without `controllerAs`
([16dcce](https://github.com/angular/angular.js/commit/16dccea8873b06285d4ec6eb3bb8e96ccbd3b64e)
[#15088](https://github.com/angular/angular.js/issues/15088))
- do not overwrite values set in `$onInit()` for `<`-bound literals
([a1bdff](https://github.com/angular/angular.js/commit/a1bdffa12f82e838dee5492956b380df7e54cdf9)
[#15118](https://github.com/angular/angular.js/issues/15118))
- avoid calling `$onChanges()` twice for `NaN` initial values
([7d7efb](https://github.com/angular/angular.js/commit/7d7efbf545c8c07713eb45301660dcfca4121445))
- disallow linking the same element more than once
([1e1fbc](https://github.com/angular/angular.js/commit/1e1fbc75f5e20e8541f517a5cf6f30f8f2eed53f))
- correctly merge consecutive text nodes on IE11
@@ -136,14 +466,14 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
[#5513](https://github.com/angular/angular.js/issues/5513),
[#5597](https://github.com/angular/angular.js/issues/5597))
- move check for interpolation of on-event attributes to compile time
([b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4),
[#13267](https://github.com/angular/angular.js/issues/13267))
([b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4),
[#13267](https://github.com/angular/angular.js/issues/13267))
- **select, ngOptions, ngValue:**
- don't add comment nodes as empty options
([245b27](https://github.com/angular/angular.js/commit/245b27101aad129061585252b73652054319ca82),
[#15454](https://github.com/angular/angular.js/issues/15454))
- do not throw when removing the element (e.g. via `ngIf`)
([7a667c](https://github.com/angular/angular.js/commit/7a667c77e36f2b1738425a9cfb52d48bb9d8220f))
([7a667c](https://github.com/angular/angular.js/commit/7a667c77e36f2b1738425a9cfb52d48bb9d8220f))
- add/remove selected attribute for selected/unselected options
([c75698](https://github.com/angular/angular.js/commit/c75698df55f5a026bcd7fcecbb9d4ff0bc3ebc3e))
- don't register options when select has no ngModel
@@ -158,7 +488,7 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
([e6afca](https://github.com/angular/angular.js/commit/e6afca00c9061a3e13b570796ca3ab428c1723a1),
[#14031](https://github.com/angular/angular.js/issues/14031))
- **$resource:**
- **$resource:** allow params in `hostname` (except for IPv6 addresses)
- allow params in `hostname` (except for IPv6 addresses)
([752b1e](https://github.com/angular/angular.js/commit/752b1e69b7a8e9c0b908f1980e9c738888f3647c),
[#14542](https://github.com/angular/angular.js/issues/14542))
- fulfill promise with the correct value on error
@@ -202,6 +532,9 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
- **loader:** `module.decorator` order of operations is now irrelevant
([6a2ebd](https://github.com/angular/angular.js/commit/6a2ebdba5df27e789e3cb10f11eedf90f7b9b97e),
[#12382](https://github.com/angular/angular.js/issues/12382))
- **$sanitize:** reduce stack height in IE <= 11
([45129c](https://github.com/angular/angular.js/commit/45129cfd06104bd89f469dded9ccbaf20894bd76)
[#14928](https://github.com/angular/angular.js/issues/14928))
- **ngAnimate:** make svg elements work with `classNameFilter`
([81bf7e](https://github.com/angular/angular.js/commit/81bf7ed73ee67f9eb997da869c52839449ca02b3))
@@ -221,8 +554,11 @@ consolidating all the changes shown in the previous 1.6.0 release candidates.**
([d71dc2](https://github.com/angular/angular.js/commit/d71dc2f5afec230711351e9f160873a41eb60597))
- **injector:** cache the results of the native class detection check
([5ceb5d](https://github.com/angular/angular.js/commit/5ceb5dbfa6d9b6d15232a1f5c767b2f431325948))
- **$compile:** use strict comparison for `controller === '@'`
([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f))
- **$compile:**
- use strict comparison for `controller === '@'`
([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f))
- validate `directive.restrict` property on directive init
([11f273](https://github.com/angular/angular.js/commit/11f2731f72e932615e8ce15e6a73f4ac808cc7e7))
- **$parse:**
- Inline constants
([bd7d5f](https://github.com/angular/angular.js/commit/bd7d5f6345439aa2d1da708ffee20b4c565131d4))
@@ -452,6 +788,48 @@ var bgColor = elem.css('background-color');
var bgColor = elem.css('backgroundColor');
```
- **[7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304)**: don't get/set properties when getting/setting boolean attributes
Previously, all boolean attributes were reflected into the corresponding property when calling a
setter and from the corresponding property when calling a getter, even on elements that don't treat
those attributes in a special way. Now Angular doesn't do it by itself, but relies on browsers to
know when to reflect the property. Note that this browser-level conversion differs between browsers;
if you need to dynamically change the state of an element, you should modify the property, not the
attribute. See https://jquery.com/upgrade-guide/1.9/#attr-versus-prop- for a more detailed
description about a related change in jQuery 1.9.
This change aligns jqLite with jQuery 3. To migrate the code follow the example below:
Before:
CSS:
```css
input[checked="checked"] { ... }
```
JS:
```js
elem1.attr('checked', 'checked');
elem2.attr('checked', false);
```
After:
CSS:
```css
input:checked { ... }
```
JS:
```js
elem1.prop('checked', true);
elem2.prop('checked', false);
```
- **[3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)**:
don't remove a boolean attribute for `.attr(attrName, '')`
@@ -875,7 +1253,7 @@ previous behaviour simply add a comment:
**Note:** Everything described below affects **IE11 only**.
Previously, consecutive text nodes would not get merged if they had no parent. They will now, which
might have unexpectd side effects in the following cases:
might have unexpected side effects in the following cases:
1. Passing an array or jqLite/jQuery collection of parent-less text nodes to `$compile` directly:
@@ -1029,7 +1407,7 @@ In cases where `ngView` was loaded asynchronously, `$route` (and its dependencie
might also have been instantiated asynchronously. After this change, `$route` (and its dependencies)
will - by default - be instantiated early on.
Although this is not expected to have unwanted side-effects in normal application bebavior, it may
Although this is not expected to have unwanted side-effects in normal application behavior, it may
affect your unit tests: When testing a module that (directly or indirectly) depends on `ngRoute`, a
request will be made for the default route's template. If not properly "trained", `$httpBackend`
will complain about this unexpected request.
@@ -1595,6 +1973,7 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
- **ngModel:** treat synchronous validators as boolean always ([7bc71a](https://github.com/angular/angular.js/commit/7bc71adc63bb6bb609b44dd2d3ea8fb0cd3f300b) [#14734](https://github.com/angular/angular.js/issues/14734))
- **$q:** treat thrown errors as regular rejections ([e13eea](https://github.com/angular/angular.js/commit/e13eeabd7e34a78becec06cfbe72c23f2dcb85f9) [#3174](https://github.com/angular/angular.js/issues/3174) [#15213](https://github.com/angular/angular.js/issues/15213))
- **ngTransclude:** use fallback content if only whitespace is provided ([32aa7e](https://github.com/angular/angular.js/commit/32aa7e7395527624119e3917c54ee43b4d219301) [#15077](https://github.com/angular/angular.js/issues/15077))
- **$location:** prevent infinite digest with IDN URLs in Edge ([705afc](https://github.com/angular/angular.js/commit/705afcd160c8428133b36f2cd63db305dc52f2d7) [#15217](https://github.com/angular/angular.js/issues/15217))
- **$compile:**
- don't throw tplrt error when there is a whitespace around a top-level comment ([76d3da](https://github.com/angular/angular.js/commit/76d3dafdeaf2f343d094b5a34ffb74adf64bb284) [#15108](https://github.com/angular/angular.js/issues/15108))
- disallow linking the same element more than once ([1e1fbc](https://github.com/angular/angular.js/commit/1e1fbc75f5e20e8541f517a5cf6f30f8f2eed53f))
@@ -1604,6 +1983,20 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
- don't add leading white-space in attributes for a specific merge case ([305ba1](https://github.com/angular/angular.js/commit/305ba1a3fb3529cb3fdf04c12ac03fbb4f634456))
- don't trim white-space in attributes ([97bbf8](https://github.com/angular/angular.js/commit/97bbf86a1979d099802f0d631c17c54b87563b40) [#5513](https://github.com/angular/angular.js/issues/5513) [#5597](https://github.com/angular/angular.js/issues/5597))
- move check for interpolation of on-event attributes to compile time ([b89c21](https://github.com/angular/angular.js/commit/b89c2181a9a165e06c027390164e08635ec449f4) [#13267](https://github.com/angular/angular.js/issues/13267))
- clean up `@`-binding observers when re-assigning bindings
([586e2a](https://github.com/angular/angular.js/commit/586e2acb269016a0fee66ac33f4a385f631afad0)
[#15268](https://github.com/angular/angular.js/issues/15268))
- set attribute value even if `ngAttr*` contains no interpolation
([3fe3da](https://github.com/angular/angular.js/commit/3fe3da8794571a1479d884be26a621f06cdb7842)
[#15133](https://github.com/angular/angular.js/issues/15133))
- `bindToController` should work without `controllerAs`
([16dcce](https://github.com/angular/angular.js/commit/16dccea8873b06285d4ec6eb3bb8e96ccbd3b64e)
[#15088](https://github.com/angular/angular.js/issues/15088))
- do not overwrite values set in `$onInit()` for `<`-bound literals
([a1bdff](https://github.com/angular/angular.js/commit/a1bdffa12f82e838dee5492956b380df7e54cdf9)
[#15118](https://github.com/angular/angular.js/issues/15118))
- avoid calling `$onChanges()` twice for `NaN` initial values
([7d7efb](https://github.com/angular/angular.js/commit/7d7efbf545c8c07713eb45301660dcfca4121445))
- **select:**
- add/remove selected attribute for selected/unselected options ([c75698](https://github.com/angular/angular.js/commit/c75698df55f5a026bcd7fcecbb9d4ff0bc3ebc3e))
- don't register options when select has no ngModel ([e8c2e1](https://github.com/angular/angular.js/commit/e8c2e119758e58e18fe43932d09a8ff9f506aa9d))
@@ -1627,6 +2020,9 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
- **ngMock/$httpBackend:** fail if a url is provided but is `undefined` ([7551b8](https://github.com/angular/angular.js/commit/7551b8975a91ee286cc2cf4af5e78f924533575e) [#8442](https://github.com/angular/angular.js/issues/8442) [#10934](https://github.com/angular/angular.js/issues/10934))
- **$route:** don't process route change controllers and templates for `redirectTo` routes ([7f4b35](https://github.com/angular/angular.js/commit/7f4b356c2bebb87f0c26b57a20415b004b20bcd1) [#3332](https://github.com/angular/angular.js/issues/3332))
- **loader:** `module.decorator` order of operations is now irrelevant ([6a2ebd](https://github.com/angular/angular.js/commit/6a2ebdba5df27e789e3cb10f11eedf90f7b9b97e) [#12382](https://github.com/angular/angular.js/issues/12382))
- **$sanitize:** reduce stack height in IE <= 11
([45129c](https://github.com/angular/angular.js/commit/45129cfd06104bd89f469dded9ccbaf20894bd76)
[#14928](https://github.com/angular/angular.js/issues/14928))
- **ngAnimate:** make svg elements work with `classNameFilter` ([81bf7e](https://github.com/angular/angular.js/commit/81bf7ed73ee67f9eb997da869c52839449ca02b3))
@@ -1634,24 +2030,50 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
- **jqLite:**
- implement `jqLite(f)` as an alias to `jqLite(document).ready(f)` ([369fb7](https://github.com/angular/angular.js/commit/369fb7f4f73664bcdab0350701552d8bef6f605e))
- don't throw for elements with missing `getAttribute` ([4e6c14](https://github.com/angular/angular.js/commit/4e6c14dcae4a9a30b3610a288ef8d20db47c4417))
- don't get/set properties when getting/setting boolean attributes ([7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304), [#14126](https://github.com/angular/angular.js/issues/14126))
- don't remove a boolean attribute for `.attr(attrName, '')` ([3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a))
- remove the attribute for `.attr(attribute, null)` ([4e3624](https://github.com/angular/angular.js/commit/4e3624552284d0e725bf6262b2e468cd2c7682fa))
- return `[]` for `.val()` on `<select multiple>` with no selection ([d882fd](https://github.com/angular/angular.js/commit/d882fde2e532216e7cf424495db1ccb5be1789f8))
- **$compile:**
- add `preAssignBindingsEnabled` option
([dfb8cf](https://github.com/angular/angular.js/commit/dfb8cf6402678206132e5bc603764d21e0f986ef))
- throw error when directive name or factory function is invalid
([53a3bf](https://github.com/angular/angular.js/commit/53a3bf6634600c3aeff092eacc35edf399b27aec)
[#15056](https://github.com/angular/angular.js/issues/15056))
- **$http:**
- remove deprecated callback methods: `success()/error()` ([b54a39](https://github.com/angular/angular.js/commit/b54a39e2029005e0572fbd2ac0e8f6a4e5d69014))
- JSONP callback must be specified by `jsonpCallbackParam` config ([fb6634](https://github.com/angular/angular.js/commit/fb663418710736161a6b5da49c345e92edf58dcb) [#15161](https://github.com/angular/angular.js/issues/15161) [#11352](https://github.com/angular/angular.js/issues/11352))
- JSONP requests now require a trusted resource URL ([6476af](https://github.com/angular/angular.js/commit/6476af83cd0418c84e034a955b12a842794385c4) [#11352](https://github.com/angular/angular.js/issues/11352))
- **$anchorScroll:** convert numeric hash targets to string
([9062ba](https://github.com/angular/angular.js/commit/9062bae05c002934fe7bfd76043dcc3de9acfde6)
[#14680](https://github.com/angular/angular.js/issues/14680))
- **ngModelOptions:** allow options to be inherited from ancestor `ngModelOptions` ([87a2ff](https://github.com/angular/angular.js/commit/87a2ff76af5d0a9268d8eb84db5755077d27c84c) [#10922](https://github.com/angular/angular.js/issues/10922))
- **input:**
- add support for binding to `input[type=range]` ([913016](https://github.com/angular/angular.js/commit/9130166767c4792c5d32d08a918fc7becf32c9a6) [#5892](https://github.com/angular/angular.js/issues/5892) [#14870](https://github.com/angular/angular.js/issues/14870))
- add support for `step` to `input[type=number]` ([e1da4be](https://github.com/angular/angular.js/commit/e1da4bed8e291003d485a8ad346ab80bed8ae2e3) [#10597](https://github.com/angular/angular.js/issues/10597))
- allow `ngTrim` to work for `input[type=radio]` ([47724b](https://github.com/angular/angular.js/commit/47724baffe050269385b3481e9a9cf4ab3944b4b))
- **ngSwitch:** allow multiple case matches via optional attribute `ngSwitchWhenSeparator`
([0b221](https://github.com/angular/angular.js/commit/0b22173000596bf4b78f6a90083b994d46164d79)
[#3410](https://github.com/angular/angular.js/issues/3410)
[#3516](https://github.com/angular/angular.js/issues/3516))
- **ngRoute:** allow `ngView` to be included in an asynchronously loaded template ([c13c66](https://github.com/angular/angular.js/commit/c13c666728c1a1485ef18e92d7cb35118ce39609) [#1213](https://github.com/angular/angular.js/issues/1213))
- **select:** support values of any type added with `ngValue` ([f02b70](https://github.com/angular/angular.js/commit/f02b707b5e4a5ffd1e1a20d910754cfabfc19622) [#9842](https://github.com/angular/angular.js/issues/9842))
- **$interpolate:** use custom `toString()` function if present ([a5fd2e](https://github.com/angular/angular.js/commit/a5fd2e4c0376676fa317e09a8d8be4966b82cbfe) [#7317](https://github.com/angular/angular.js/issues/7317) [#11406](https://github.com/angular/angular.js/issues/11406))
- **$route:** implement `resolveRedirectTo` ([e98656](https://github.com/angular/angular.js/commit/e9865654b39c71be71034c38581a8c7bd16bc716) [#5150](https://github.com/angular/angular.js/issues/5150))
- **$q:** report promises with non rejection callback ([c9dffd](https://github.com/angular/angular.js/commit/c9dffde1cb167660120753181cb6d01dc1d1b3d0) [#13653](https://github.com/angular/angular.js/issues/13653) [#7992](https://github.com/angular/angular.js/issues/7992))
- **$location:** default hashPrefix to `'!'` ([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52) [#13812](https://github.com/angular/angular.js/issues/13812))
- **$resource:** pass `status`/`statusText` to success callbacks
([e3a378](https://github.com/angular/angular.js/commit/e3a378e7a329f60f6b48517f83a4f4c9efecb056)
[#8341](https://github.com/angular/angular.js/issues/8841)
[#8841](https://github.com/angular/angular.js/issues/8841))
- **$location:**
- default hashPrefix to `'!'`
([aa077e](https://github.com/angular/angular.js/commit/aa077e81129c740041438688dff2e8d20c3d7b52)
[#13812](https://github.com/angular/angular.js/issues/13812))
- add support for selectively rewriting links based on attribute
([3d686a](https://github.com/angular/angular.js/commit/3d686a988dc4373da094cff6905e5b0d8da6afa4))
- **$controller:** throw when requested controller is not registered
([eacfe4](https://github.com/angular/angular.js/commit/eacfe4148eb97e550117ed7fd3c37b58537a9f64)
[#14980](https://github.com/angular/angular.js/issues/14980))
## Performance Improvements
@@ -1660,7 +2082,11 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
- **$animate:** listen for document visibility changes ([d71dc2](https://github.com/angular/angular.js/commit/d71dc2f5afec230711351e9f160873a41eb60597))
- **injector:** cache the results of the native class detection check ([5ceb5d](https://github.com/angular/angular.js/commit/5ceb5dbfa6d9b6d15232a1f5c767b2f431325948))
- **$parse:** Inline constants ([bd7d5f](https://github.com/angular/angular.js/commit/bd7d5f6345439aa2d1da708ffee20b4c565131d4))
- **$compile:** use strict comparison for `controller === '@'` ([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f))
- **$compile:**
- use strict comparison for `controller === '@'`
([bbd3db](https://github.com/angular/angular.js/commit/bbd3db14f857aab996ad129f2f15ca6348e9fd9f))
- validate `directive.restrict` property on directive init
([11f273](https://github.com/angular/angular.js/commit/11f2731f72e932615e8ce15e6a73f4ac808cc7e7))
- **$parse:** remove Angular expression sandbox ([1547c7](https://github.com/angular/angular.js/commit/1547c751aa48efe7dbefef701c3df5983b04aa2e) [#15094](https://github.com/angular/angular.js/issues/15094))
@@ -1782,6 +2208,48 @@ var bgColor = elem.css('background-color');
var bgColor = elem.css('backgroundColor');
```
- **[7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304)**: don't get/set properties when getting/setting boolean attributes
Previously, all boolean attributes were reflected into the corresponding property when calling a
setter and from the corresponding property when calling a getter, even on elements that don't treat
those attributes in a special way. Now Angular doesn't do it by itself, but relies on browsers to
know when to reflect the property. Note that this browser-level conversion differs between browsers;
if you need to dynamically change the state of an element, you should modify the property, not the
attribute. See https://jquery.com/upgrade-guide/1.9/#attr-versus-prop- for a more detailed
description about a related change in jQuery 1.9.
This change aligns jqLite with jQuery 3. To migrate the code follow the example below:
Before:
CSS:
```css
input[checked="checked"] { ... }
```
JS:
```js
elem1.attr('checked', 'checked');
elem2.attr('checked', false);
```
After:
CSS:
```css
input:checked { ... }
```
JS:
```js
elem1.prop('checked', true);
elem2.prop('checked', false);
```
- **[3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)**: don't remove a boolean attribute for `.attr(attrName, '')`
Before, using the `attr` method with an empty string as a value
@@ -2215,7 +2683,7 @@ affects custom directives that might have been reading options for their own pur
**Note:** Everything described below affects **IE11 only**.
Previously, consecutive text nodes would not get merged if they had no parent. They will now, which
might have unexpectd side effects in the following cases:
might have unexpected side effects in the following cases:
1. Passing an array or jqLite/jQuery collection of parent-less text nodes to `$compile` directly:
@@ -2365,7 +2833,7 @@ In cases where `ngView` was loaded asynchronously, `$route` (and its dependencie
might also have been instantiated asynchronously. After this change, `$route` (and its dependencies)
will - by default - be instantiated early on.
Although this is not expected to have unwanted side-effects in normal application bebavior, it may
Although this is not expected to have unwanted side-effects in normal application behavior, it may
affect your unit tests: When testing a module that (directly or indirectly) depends on `ngRoute`, a
request will be made for the default route's template. If not properly "trained", `$httpBackend`
will complain about this unexpected request.
@@ -4273,6 +4741,7 @@ support asynchronous loading of resources.)
- **$compile:** Lazily compile the `transclude` function
([652b83eb](https://github.com/angular/angular.js/commit/652b83eb226131d131a44453520a569202aa4aac))
See https://github.com/angular/angular.js/issues/14343#issuecomment-229037252 for more information.
## Breaking Changes
@@ -12079,7 +12548,7 @@ Contains only these fixes cherry-picked from [v1.2.0rc1](#1.2.0rc1).
- due to [39841f2e](https://github.com/angular/angular.js/commit/39841f2ec9b17b3b2920fd1eb548d444251f4f56),
Interpolations inside DOM event handlers are disallowed.
DOM event handlers execute arbitrary Javascript code. Using an interpolation for such handlers means that the interpolated value is a JS string that is evaluated. Storing or generating such strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other Angular specific event handlers evaluate Angular expressions in non-window (Scope) context which makes them much safer.
DOM event handlers execute arbitrary JavaScript code. Using an interpolation for such handlers means that the interpolated value is a JS string that is evaluated. Storing or generating such strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other Angular specific event handlers evaluate Angular expressions in non-window (Scope) context which makes them much safer.
To migrate the code follow the example below:
@@ -14710,7 +15179,7 @@ with the `$route` service
### Docs
- rewrite of several major portions of angular.service.*, angular.Array.*, angular.Object.* docs
- added support for [sitemap]((http://docs.angularjs.org/sitemap.xml) to make the docs indexable by
- added support for [sitemap](http://docs.angularjs.org/sitemap.xml) to make the docs indexable by
search crawlers
- transition of Developer Guide docs from the wiki into docs.angularjs.org
- lots of improvements related to formatting of the content of docs.anguarjs.org
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2010-2016 Google, Inc. http://angularjs.org
Copyright (c) 2010-2017 Google, Inc. http://angularjs.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+44
View File
@@ -0,0 +1,44 @@
'use strict';
angular
.module('animationBenchmark', ['ngAnimate'], config)
.controller('BenchmarkController', BenchmarkController);
// Functions - Definitions
function config($compileProvider) {
$compileProvider
.commentDirectivesEnabled(false)
.cssClassDirectivesEnabled(false)
.debugInfoEnabled(false);
}
function BenchmarkController($scope) {
var self = this;
var itemCount = 1000;
var items = (new Array(itemCount + 1)).join('.').split('');
benchmarkSteps.push({
name: 'create',
fn: function() {
$scope.$apply(function() {
self.items = items;
});
}
});
benchmarkSteps.push({
name: '$digest',
fn: function() {
$scope.$root.$digest();
}
});
benchmarkSteps.push({
name: 'destroy',
fn: function() {
$scope.$apply(function() {
self.items = [];
});
}
});
}
+22
View File
@@ -0,0 +1,22 @@
/* eslint-env node */
'use strict';
module.exports = function(config) {
config.set({
scripts: [
{
id: 'jquery',
src: 'jquery-noop.js'
}, {
id: 'angular',
src: '/build/angular.js'
}, {
id: 'angular-animate',
src: '/build/angular-animate.js'
}, {
src: 'app.js'
}
]
});
};
+1
View File
@@ -0,0 +1 @@
// Override me with ?jquery=/bower_components/jquery/dist/jquery.js
+28
View File
@@ -0,0 +1,28 @@
<style>
[ng-cloak] { display: none !important; }
.animation-container .ng-enter,
.animation-container .ng-leave {
transition: all 0.1s;
}
.animation-container .ng-enter,
.animation-container .ng-leave.ng-leave-active {
opacity: 0;
}
.animation-container .ng-enter.ng-enter-active,
.animation-container .ng-leave {
opacity: 1;
}
</style>
<div ng-app="animationBenchmark" ng-cloak ng-controller="BenchmarkController as bm">
<div class="container-fluid">
<h2>Large collection of elements animated in and out with ngAnimate</h2>
<div class="animation-container">
<div ng-repeat="i in bm.items track by $index">
Just a plain ol' element
</div>
</div>
</div>
</div>
+108
View File
@@ -0,0 +1,108 @@
'use strict';
var app = angular.module('ngClassBenchmark', []);
app.controller('DataController', function DataController($scope) {
this.init = function() {
this.numberOfTodos = 1000;
this.implementation = 'tableOptimized';
this.completedPeriodicity = 3;
this.importantPeriodicity = 13;
this.urgentPeriodicity = 29;
this.createTodos(100);
this.setTodosValuesWithSeed(0);
};
this.clearTodos = function() {
this.todos = null;
};
this.createTodos = function(count) {
var i;
this.todos = [];
for (i = 0; i < count; i++) {
this.todos.push({
id: i + 1,
completed: false,
important: false,
urgent: false
});
}
};
this.setTodosValuesWithSeed = function(offset) {
var i, todo;
for (i = 0; i < this.todos.length; i++) {
todo = this.todos[i];
todo.completed = 0 === (i + offset) % this.completedPeriodicity;
todo.important = 0 === (i + offset) % this.importantPeriodicity;
todo.urgent = 0 === (i + offset) % this.urgentPeriodicity;
}
};
this.init();
benchmarkSteps.push({
name: 'setup',
fn: function() {
$scope.$apply();
this.clearTodos();
this.createTodos(this.numberOfTodos);
}.bind(this)
});
benchmarkSteps.push({
name: 'create',
fn: function() {
// initialize data for first time that will construct the DOM
this.setTodosValuesWithSeed(0);
$scope.$apply();
}.bind(this)
});
benchmarkSteps.push({
name: '$apply',
fn: function() {
$scope.$apply();
}
});
benchmarkSteps.push({
name: 'update',
fn: function() {
// move everything but completed
this.setTodosValuesWithSeed(3);
$scope.$apply();
}.bind(this)
});
benchmarkSteps.push({
name: 'unclass',
fn: function() {
// remove all classes
this.setTodosValuesWithSeed(NaN);
$scope.$apply();
}.bind(this)
});
benchmarkSteps.push({
name: 'class',
fn: function() {
// add all classes as the initial state
this.setTodosValuesWithSeed(0);
$scope.$apply();
}.bind(this)
});
benchmarkSteps.push({
name: 'destroy',
fn: function() {
this.clearTodos();
$scope.$apply();
}.bind(this)
});
});
+15
View File
@@ -0,0 +1,15 @@
/* eslint-env node */
'use strict';
module.exports = function(config) {
config.set({
scripts: [{
id: 'angular',
src: '/build/angular.js'
},
{
src: 'app.js'
}]
});
};
+177
View File
@@ -0,0 +1,177 @@
<style>
.gold {
background: gold;
}
.silver {
background: silver;
}
.table tbody tr > td.success {
background-color: #dff0d8;
}
.table tbody tr > td.error {
background-color: #f2dede;
}
.table tbody tr > td.warning {
background-color: #fcf8e3;
}
.table tbody tr > td.info {
background-color: #d9edf7;
}
.completed {
text-decoration: line-through;
}
.important {
font-weight: bold;
}
.urgent {
color: red;
}
</style>
<div ng-app="ngClassBenchmark" ng-cloak class="container-fluid">
<div ng-controller="DataController as benchmark" class="row">
<div class="col-lg-12">
<div class="well">
<h3>Parameters</h3>
<br>
<p>
<label>Number of todos</label><br>
<input type="number" ng-model="benchmark.numberOfTodos">
</p>
<br>
<p>
<label>Implementation</label><br>
<div class="radio">
<label>
<input ng-model="benchmark.implementation" value="tableOptimized"
type="radio" name="implementation">
Table optimized <br>
<code>ng-class="todo.completed && 'success'"</code>
</label>
</div>
<div class="radio">
<label>
<input ng-model="benchmark.implementation" value="table"
type="radio" name="implementation">
Table <br>
<code>ng-class="{success: todo.completed}"</code>
</label>
</div>
<div class="radio">
<label>
<input ng-model="benchmark.implementation" value="list"
type="radio" name="implementation">
List <br>
<code>ng-class="{completed: todo.completed, urgent: todo.urgent, important: todo.important"}</code>
</label>
</div>
<div class="radio">
<label>
<input ng-model="benchmark.implementation" value="singleOptimized"
type="radio" name="implementation">
Single ngClass optimized <br>
<code>
ng-class="{'panel-success': !!benchmark.todos, 'panel-danger': !benchmark.todos}"
</code>
</label>
</div>
<div class="radio">
<label>
<input ng-model="benchmark.implementation" value="single"
type="radio" name="implementation">
Single ngClass <br>
<code>
ng-class="{'panel-success': benchmark.todos, 'panel-danger': !benchmark.todos}"
</code>
</label>
</div>
</p>
</div>
<br>
<h3>Example</h3>
<div ng-switch="benchmark.implementation">
<table ng-switch-when="tableOptimized" class="table">
<thead>
<tr>
<th>todo #id</th>
<th>completed?</th>
<th>urgent?</th>
<th>important?</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="todo in benchmark.todos track by todo.id"
ng-class="todo.completed && 'active'"
ng-class-even="todo.completed && todo.important && 'gold'"
ng-class-odd="todo.completed && todo.important && 'silver'"
>
<td>#{{todo.id}}</td>
<td>{{todo.completed}}</td>
<td ng-class="todo.urgent && 'danger'">{{todo.urgent}}</td>
<td ng-class="todo.important && 'success'">{{todo.important}}</td>
</tr>
</tbody>
</table>
<table ng-switch-when="table" class="table">
<thead>
<tr>
<th>todo #id</th>
<th>completed?</th>
<th>urgent?</th>
<th>important?</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="todo in benchmark.todos track by todo.id"
ng-class="{active: todo.completed}"
ng-class-even="{gold: todo.completed && todo.important}"
ng-class-odd="{silver: todo.completed && todo.important}"
>
<td>#{{todo.id}}</td>
<td>{{todo.completed}}</td>
<td ng-class="{danger: todo.urgent}">{{todo.urgent}}</td>
<td ng-class="{success: todo.important}">{{todo.important}}</td>
</tr>
</tbody>
</table>
<ul ng-switch-when="list">
<li ng-repeat="todo in benchmark.todos track by todo.id"
ng-class="{
completed: todo.completed,
urgent: todo.urgent,
important: todo.important
}">#{{todo.id}}</li>
</ul>
<div ng-switch-when="singleOptimized"
class="panel"
ng-class="{'panel-success': !!benchmark.todos, 'panel-danger': !benchmark.todos}">
<div class="panel-heading">
<h3 class="panel-title">Information</h3>
</div>
<div class="panel-body"> The title is green because there are todos... </div>
</div>
<div ng-switch-when="single"
class="panel"
ng-class="{'panel-success': benchmark.todos, 'panel-danger': !benchmark.todos}">
<div class="panel-heading">
<h3 class="panel-title">Information</h3>
</div>
<div class="panel-body"> The title is green because there are todos... </div>
</div>
</div>
</div>
</div>
</div>
<br><br><br>
+1
View File
@@ -0,0 +1 @@
.visible-phone{display:none}.visible-desktop{display:block}.navbar{display:block}.navbar .container{padding:0 16px;width:auto}.navbar .brand{float:left;margin:8px 80px 0 8px;padding:0}.navbar .brand a{display:block;height:30px;margin:6px 0 5px 0;overflow:hidden;padding:0;width:117px}.navbar .nav{float:right}.navbar .nav .dropdown-toggle{color:rgba(255,255,255,0.87);font-size:16px;font-weight:300;line-height:56px;padding:0 24px;text-transform:uppercase;transition:all .3s}.navbar .nav .dropdown-toggle:hover,.navbar .nav .dropdown-toggle:active,.navbar .nav .dropdown-toggle:focus{background:#37474F;color:#fff}.navbar .nav .dropdown-menu{background:#37474F;border:none;border-radius:0;box-shadow:0 0 16px rgba(0,0,0,0.12),0 16px 16px rgba(0,0,0,0.24);color:#fff;left:auto;margin:0;padding:0;right:0}.navbar .nav .dropdown-menu:after,.navbar .nav .dropdown-menu:before{display:none}.navbar .nav .dropdown-menu li{border-bottom:1px solid rgba(38,50,56,0.56);box-sizing:border-box;line-height:48px}.navbar .nav .dropdown-menu li:last-child{border:none}.navbar .nav .dropdown-menu a{background:#37474F;color:#fff;font-weight:300;line-height:48px;padding:0 16px;transition:all .2s}.navbar .nav .dropdown-menu a:hover,.navbar .nav .dropdown-menu a:focus{background:#455A64}.navbar .navbar-search{left:200px;margin:0;position:absolute;right:440px;top:8px;width:auto}.navbar .navbar-search i{color:#546E7A;font-size:16px;left:12px;position:absolute;top:11px}.navbar .navbar-search .search-query{background:#37474F;border:none;border-radius:2px;box-shadow:none;box-sizing:border-box;color:#546E7A;font-size:14px;height:40px;width:100%;padding:0 16px 0 32px;text-shadow:none;transition:all .3s}.navbar .navbar-search .search-query:-webkit-autofill,.navbar .navbar-search .search-query:-webkit-autofill:hover,.navbar .navbar-search .search-query:-webkit-autofill:focus{background-color:#fff;transition:background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#455A64}.navbar .navbar-search .search-query:hover,.navbar .navbar-search .search-query:active,.navbar .navbar-search .search-query:focus{background:#fff;box-shadow:inset 0 2px 4px rgba(0,0,0,0.24);color:#2196F3}.navbar .navbar-search .search-query::-webkit-input-placeholder{color:#546E7A}.navbar .navbar-search .search-query::-moz-placeholder{color:#546E7A}.navbar .navbar-search .search-query:-ms-input-placeholder{color:#546E7A}.navbar .navbar-search .search-query:-moz-placeholder{color:#546E7A}#navbar-main .navbar-inner{background:#263238;height:56px}#navbar-notice{z-index:1029;top:56px}#navbar-notice .navbar-inner{background:#ECEFF1;box-shadow:0 0 3px rgba(0,0,0,0.12),0 3px 3px rgba(0,0,0,0.24);height:auto}.site-notice{padding:4px 0;text-align:center;font-size:13px;margin:0}@media handheld and (max-width: 800px), screen and (max-device-width: 800px), screen and (max-width: 800px){.visible-phone{display:block}.visible-desktop{display:none}}@media handheld and (max-width: 800px), screen and (max-device-width: 800px), screen and (max-width: 800px){.homepage .container{padding:16px;width:auto}.homepage .span1{width:auto}.homepage .span2{width:auto}.homepage .span3{width:auto}.homepage .span4{width:auto}.homepage .span5{width:auto}.homepage .span6{width:auto}.homepage .span7{width:auto}.homepage .span8{width:auto}.homepage .span9{width:auto}.homepage .span10{width:auto}.homepage .navbar .container{padding:0 8px}.homepage #navbar-main .navbar-inner{height:40px}.homepage #navbar-main .brand{margin:6px 0 0 0}.homepage #navbar-main .brand a{margin:0}.homepage #navbar-main .nav{margin:0}.homepage #navbar-main .nav .dropdown-toggle{font-size:12px;line-height:40px;padding:0 8px}.homepage #navbar-main .dropdown-menu a{padding:0 8px}.homepage #navbar-main .navbar-search{background:#263238;border-bottom:1px solid #263238;left:0;right:0;top:100%}.homepage #navbar-main .navbar-search i{left:12px;top:7px}.homepage #navbar-main .navbar-search .search-query{border-radius:0;height:32px}.homepage #navbar-notice{top:40px}.homepage #navbar-notice .site-notice{font-size:11px}.homepage .hero{padding:80px 32px 32px 32px}.homepage .hero h2{background-size:230px 60px;height:60px;width:230px}}
+97 -6
View File
@@ -53,13 +53,13 @@ h1,h2,h3,h4,h5,h6 {
}
.header .brand {
padding-top: 6px;
padding-bottom: 0px;
}
.header .brand img {
margin-top: 5px;
height: 30px;
margin-top: 0;
height: auto;
vertical-align: top;
}
.docs-search {
@@ -82,6 +82,11 @@ h1,h2,h3,h4,h5,h6 {
margin-right: 10px;
}
.navbar .navbar-search i {
top: 13px;
font-size: 12px;
}
.docs-search > .search-query:focus {
outline: 0;
}
@@ -297,6 +302,7 @@ iframe.example {
}
.search-results-container {
position: relative;
padding-bottom: 1em;
border-top: 1px solid #111;
background: #181818;
@@ -435,15 +441,17 @@ iframe.example {
background: #f1f1f1;
}
.sup-header {
#navbar-sub {
padding-top: 10px;
padding-bottom: 5px;
background: rgba(245,245,245,0.88);
box-shadow: 0 0 2px #999;
z-index: 1028;
top: 83px;
}
.main-body-grid {
margin-top: 120px;
margin-top: 144px;
position: relative;
}
@@ -454,7 +462,7 @@ iframe.example {
.main-body-grid > .grid-left {
position: fixed;
top: 120px;
top: 144px;
bottom: 0;
overflow: auto;
}
@@ -827,3 +835,86 @@ ul.events > li {
iframe[name="example-anchoringExample"] {
height: 400px;
}
/*
angular-topnav.css and bootstrap overrides
*/
.navbar .navbar-inner .container {
padding: 0 16px;
width: auto;
height: auto;
}
.navbar .nav > li {
float: left;
}
.navbar-nav .open .dropdown-menu {
position: absolute;
float: left;
}
.navbar-nav .open .dropdown-menu > li > a {
line-height: 48px;
}
#navbar-main .navbar-inner, #navbar-notice .navbar-inner {
box-shadow: none;
}
#navbar-sub .container {
max-width: 970px;
}
.nav .open > a, .nav .open > a:hover, .nav .open > a:focus {
background-color: inherit;
}
@media handheld and (max-width:800px), screen and (max-device-width:800px), screen and (max-width:800px) {
.navbar {
min-height: auto;
}
.search-results-container {
top: 32px;
overflow: auto;
max-height: 85vh;
padding-bottom: 0;
position: static;
}
.search-close {
right: 1px;
margin-left: 0;
top: 41px;
padding: 5px 10px;
border-top-right-radius: 0;
border-top-left-radius: 0;
box-shadow: none;
width: auto;
bottom: auto;
left: auto;
}
.navbar-nav .open .dropdown-menu > li > a, .navbar-nav .open .dropdown-menu .dropdown-header {
padding: 0 8px;
}
.homepage #navbar-notice {
top: 72px;
}
#navbar-notice .navbar-inner {
box-shadow: 0 0 3px rgba(0, 0, 0, .12), 0 3px 3px rgba(0, 0, 0, .24)
}
#navbar-sub {
position: relative;
top: 17px;
margin-top: 80px;
padding-bottom: 0;
margin-bottom: 0;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
/* global importScripts, lunr */
// Load up the lunr library
importScripts('../components/lunr.js-0.5.12/lunr.min.js');
importScripts('../components/lunr-0.7.2/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
+2 -2
View File
@@ -18,8 +18,8 @@ describe('doc.angularjs.org', function() {
var ngBindLink = element(by.css('.definition-table td a[href="api/ng/directive/ngClick"]'));
ngBindLink.click();
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('ngClick');
var mainHeader = element(by.css('.main-body h1 '));
expect(mainHeader.getText()).toEqual('ngClick');
});
+11 -9
View File
@@ -44,30 +44,31 @@ describe('docs.angularjs.org', function() {
var ngBindLink = element(by.css('.definition-table td a[href="api/ng/directive/ngClick"]'));
ngBindLink.click();
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('ngClick');
var mainHeader = element(by.css('.main-body h1 '));
expect(mainHeader.getText()).toEqual('ngClick');
});
it('should be resilient to trailing slashes', function() {
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/');
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('angular.noop');
var mainHeader = element(by.css('.main-body h1 '));
expect(mainHeader.getText()).toEqual('angular.noop');
});
it('should be resilient to trailing "index"', function() {
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/index');
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('angular.noop');
var mainHeader = element(by.css('.main-body h1 '));
expect(mainHeader.getText()).toEqual('angular.noop');
});
it('should be resilient to trailing "index/"', function() {
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/index/');
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('angular.noop');
var mainHeader = element(by.css('.main-body h1 '));
expect(mainHeader.getText()).toEqual('angular.noop');
});
@@ -78,7 +79,8 @@ describe('docs.angularjs.org', function() {
it('should display an error if the page does not exist', function() {
browser.get('build/docs/index-production.html#!/api/does/not/exist');
expect(element(by.css('h1')).getText()).toBe('Oops!');
var mainHeader = element(by.css('.main-body h1 '));
expect(mainHeader.getText()).toEqual('Oops!');
});
});
+5 -4
View File
@@ -159,10 +159,11 @@ angular.module('examples', [])
};
// Initialize the example data, so it's ready when clicking the open button.
// Otherwise pop-up blockers will prevent a new window from opening
ctrl.prepareExampleData(ctrl.example.path);
ctrl.$onInit = function() {
// Initialize the example data, so it's ready when clicking the open button.
// Otherwise pop-up blockers will prevent a new window from opening
ctrl.prepareExampleData(ctrl.example.path);
};
}]
};
}])
-10
View File
@@ -1,10 +0,0 @@
{
"name": "AngularJS-docs-app",
"dependencies": {
"jquery": "2.2.3",
"lunr.js": "0.5.12",
"open-sans-fontface": "1.0.4",
"google-code-prettify": "1.0.1",
"bootstrap": "3.1.1"
}
}
+3 -2
View File
@@ -17,9 +17,9 @@ module.exports = function debugDeployment(getVersion) {
'../angular-sanitize.js',
'../angular-touch.js',
'../angular-animate.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'components/marked-' + getVersion('marked') + '/lib/marked.js',
'js/angular-bootstrap/dropdown-toggle.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.js',
'components/lunr-' + getVersion('lunr') + '/lunr.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
'js/current-version-data.js',
@@ -32,6 +32,7 @@ module.exports = function debugDeployment(getVersion) {
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/angular-topnav.css',
'css/docs.css',
'css/animations.css'
]
+3 -2
View File
@@ -17,9 +17,9 @@ module.exports = function defaultDeployment(getVersion) {
'../angular-sanitize.min.js',
'../angular-touch.min.js',
'../angular-animate.min.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'components/marked-' + getVersion('marked') + '/lib/marked.js',
'js/angular-bootstrap/dropdown-toggle.min.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
'components/lunr-' + getVersion('lunr') + '/lunr.min.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
'js/current-version-data.js',
@@ -32,6 +32,7 @@ module.exports = function defaultDeployment(getVersion) {
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/angular-topnav.css',
'css/docs.css',
'css/animations.css'
]
+3 -2
View File
@@ -21,9 +21,9 @@ module.exports = function jqueryDeployment(getVersion) {
'../angular-sanitize.min.js',
'../angular-touch.min.js',
'../angular-animate.min.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'components/marked-' + getVersion('marked') + '/lib/marked.js',
'js/angular-bootstrap/dropdown-toggle.min.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
'components/lunr-' + getVersion('lunr') + '/lunr.min.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
'js/current-version-data.js',
@@ -36,6 +36,7 @@ module.exports = function jqueryDeployment(getVersion) {
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/angular-topnav.css',
'css/docs.css',
'css/animations.css'
]
@@ -34,9 +34,9 @@ module.exports = function productionDeployment(getVersion) {
cdnUrl + '/angular-sanitize.min.js',
cdnUrl + '/angular-touch.min.js',
cdnUrl + '/angular-animate.min.js',
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
'components/marked-' + getVersion('marked') + '/lib/marked.js',
'js/angular-bootstrap/dropdown-toggle.min.js',
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
'components/lunr-' + getVersion('lunr') + '/lunr.min.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
'js/current-version-data.js',
@@ -49,6 +49,7 @@ module.exports = function productionDeployment(getVersion) {
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
'css/prettify-theme.css',
'css/angular-topnav.css',
'css/docs.css',
'css/animations.css'
]
+2 -2
View File
@@ -10,8 +10,8 @@ module.exports = function getVersion(readFilesProcessor) {
var basePath = readFilesProcessor.basePath;
return function(component, sourceFolder, packageFile) {
sourceFolder = path.resolve(basePath, sourceFolder || 'docs/bower_components');
packageFile = packageFile || 'bower.json';
sourceFolder = path.resolve(basePath, sourceFolder || 'node_modules');
packageFile = packageFile || 'package.json';
return require(path.join(sourceFolder,component,packageFile)).version;
};
};
@@ -68,101 +68,95 @@
})();
</script>
</head>
<body>
<body class="homepage">
<div id="wrapper">
<header scroll-y-offset-element class="header header-fixed">
<section class="navbar navbar-inverse docs-navbar-primary" ng-controller="DocsSearchCtrl">
<div class="container">
<div class="row">
<div class="col-md-9 header-branding">
<a class="brand navbar-brand" href="http://angularjs.org">
<img width="117" height="30" class="logo" alt="Link to Angular JS Homepage" ng-src="img/angularjs-for-header-only.svg">
</a>
<ul class="nav navbar-nav">
<li class="divider-vertical"></li>
<li><a href="http://angularjs.org"><i class="icon-home icon-white"></i> Home</a></li>
<li class="divider-vertical"></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="icon-eye-open icon-white"></i> Learn <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li class="disabled"><a href="http://angularjs.org/">Why AngularJS?</a></li>
<li><a href="http://www.youtube.com/user/angularjs">Watch</a></li>
<li><a href="tutorial">Tutorial</a></li>
<li><a href="https://www.madewithangular.com/">Case Studies</a></li>
<li><a href="https://github.com/angular/angular-seed">Seed App project template</a></li>
<li><a href="misc/faq">FAQ</a></li>
</ul>
</li>
<li class="divider-vertical"></li>
<li class="dropdown active">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="icon-book icon-white"></i> Develop <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="tutorial">Tutorial</a></li>
<li><a href="guide">Developer Guide</a></li>
<li><a href="api">API Reference</a></li>
<li><a href="error">Error Reference</a></li>
<li><a href="misc/contribute">Contribute</a></li>
<li><a href="http://code.angularjs.org/">Download</a></li>
</ul>
</li>
<li class="divider-vertical"></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
<i class="icon-comment icon-white"></i> Discuss <b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="http://blog.angularjs.org">Blog</a></li>
<li><a href="http://groups.google.com/group/angular">Mailing List</a></li>
<li><a href="http://webchat.freenode.net/?channels=angularjs&uio=d4">Chat Room</a></li>
<li class="divider"></li>
<li><a href="https://twitter.com/#!/angularjs">Twitter</a></li>
<li><a href="https://plus.google.com/110323587230527980117">Google+</a></li>
<li class="divider"></li>
<li><a href="https://github.com/angular/angular.js">GitHub</a></li>
<li><a href="https://github.com/angular/angular.js/issues">Issue Tracker</a></li>
</ul>
</li>
<li class="divider-vertical"></li>
</ul>
</div>
<form ng-class="{focus:focus}" class="navbar-search col-md-3 docs-search" ng-submit="submit()">
<span class="glyphicon glyphicon-search search-icon"></span>
<input type="text"
name="as_q"
class="search-query"
placeholder="Click or press / to search"
ng-focus="focus=true"
ng-blur="focus=false"
ng-change="search(q)"
ng-model="q"
docs-search-input
autocomplete="off" />
</form>
</div>
</div>
<div class="search-results-container" ng-show="hasResults">
<header class="header" scroll-y-offset-element>
<nav id="navbar-main" class="navbar navbar-fixed-top">
<div class="navbar-inner" ng-controller="DocsSearchCtrl">
<div class="container">
<div class="search-results-frame">
<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>
<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>
<h1 class="brand"><a href="http://angularjs.org"><img width="117" height="30" src="img/angularjs-for-header-only.svg" alt="AngularJS"></a></h1>
<form class="navbar-search" ng-submit="submit()">
<i class="glyphicon glyphicon-search search-icon"></i>
<input type="text" name="as_q" class="search-query" placeholder="SEARCH"
ng-focus="focus=true"
ng-blur="focus=false"
ng-change="search(q)"
ng-model="q"
ng-model-options="{debounce: 150}"
docs-search-input
autocomplete="off">
</form>
<ul class="nav navbar-nav">
<li class="dropdown" uib-dropdown>
<a href="#" class="dropdown-toggle" uib-dropdown-toggle>Learn</a>
<ul class="dropdown-menu" uib-dropdown-menu>
<li><a href="tutorial">Tutorial</a></li>
<li><a href="misc/faq">FAQ</a></li>
<li><a href="https://www.youtube.com/user/angularjs">Videos</a></li>
<li><a href="http://angular.codeschool.com/">Free Course</a></li>
<li><a href="https://www.madewithangular.com/">Case Studies</a></li>
</ul>
</li>
<li class="dropdown" uib-dropdown>
<a href="#" class="dropdown-toggle" uib-dropdown-toggle>Develop</a>
<ul class="dropdown-menu" uib-dropdown-menu>
<li><a href="guide">Developer Guide</a></li>
<li><a href="api">API Reference</a></li>
<li><a href="error">Error Reference</a></li>
<li><a href="misc/contribute">Contribute</a></li>
<li><a href="https://github.com/angular/angular-seed">Seed App project template</a></li>
<li><a href="https://github.com/angular/angular.js">GitHub</a></li>
<li><a href="http://code.angularjs.org/">Download</a></li>
</ul>
</li>
<li class="dropdown" uib-dropdown>
<a href="#" class="dropdown-toggle" uib-dropdown-toggle>Discuss</a>
<ul class="dropdown-menu" uib-dropdown-menu>
<li><a href="http://blog.angularjs.org">Blog</a></li>
<li><a href="https://twitter.com/angular">Twitter</a></li>
<li><a href="https://plus.google.com/110323587230527980117">Google+</a></li>
<li><a href="https://github.com/angular/angular.js/issues">Feature &amp; Bug Tracker</a></li>
<li><a href="http://groups.google.com/group/angular">Mailing List</a></li>
<li><a href="http://webchat.freenode.net/?channels=angularjs&uio=d4">IRC</a></li>
<li><a href="https://gitter.im/angular/angular.js">Gitter</a></li>
</ul>
</li>
</ul>
</div>
<div class="search-results-container" ng-show="hasResults">
<div class="container">
<div class="search-results-frame">
<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>
<ul class="search-results">
<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">
<span class="glyphicon glyphicon-remove search-close-icon"></span> Close
</a>
</div>
<a href="" ng-click="hideResults()" class="search-close">
<span class="glyphicon glyphicon-remove search-close-icon"></span> Close
</a>
</div>
</div>
</section>
<section class="sup-header">
</nav>
<nav id="navbar-notice" class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<p class="site-notice visible-phone">
This site refers to AngularJS (v1.x). <a href="https://angular.io/">Go to the latest Angular</a>.
</p>
<p class="site-notice visible-desktop">
This site and all of its contents are referring to AngularJS (version 1.x),
if you are looking for the latest Angular, please visit <a href="https://angular.io/">angular.io</a>.
</p>
</div>
</div>
</nav>
<nav id="navbar-sub" class="sup-header navbar navbar-fixed-top">
<div class="container main-grid main-header-grid">
<div class="grid-left">
<version-picker></version-picker>
@@ -176,7 +170,7 @@
</ul>
</div>
</div>
</section>
</nav>
</header>
<section role="main" class="container main-body">
@@ -214,7 +208,7 @@
<p class="pull-right"><a back-to-top>Back to top</a></p>
<p>
Super-powered by Google ©2010-2016
Super-powered by Google ©2010-2017
(<a id="version"
ng-href="https://github.com/angular/angular.js/blob/master/CHANGELOG.md#{{versionNumber}}"
ng-bind-template="v{{version}}" title="Changelog of this version of Angular JS">
@@ -61,12 +61,12 @@
</div>
{% endblock -%}
{% include "lib/params.template.html" %}
{% include "lib/events.template.html" %}
{%- if doc.animations %}
<h2 id="animations">Animations</h2>
{$ doc.animations | marked $}
{$ 'module:ngAnimate.$animate' | link('Click here', doc) $} to learn more about the steps involved in the animation.
{%- endif -%}
{% include "lib/params.template.html" %}
{% include "lib/events.template.html" %}
{% endblock %}
@@ -0,0 +1,8 @@
@ngdoc error
@name $animate:nongcls
@fullName `ng-animate` class not allowed
@description
This error occurs, when trying to set `$animateProvider.classNameFilter()` to a RegExp containing
the reserved `ng-animate` class. Since `.ng-animate` will be added/removed by `$animate` itself,
using it as part of the `classNameFilter` RegExp is not allowed.
+11
View File
@@ -0,0 +1,11 @@
@ngdoc error
@name $sanitize:elclob
@fullName Failed to sanitize html because the element is clobbered
@description
This error occurs when `$sanitize` sanitizer is unable to traverse the HTML because one or more of the elements in the
HTML have been "clobbered". This could be a sign that the payload contains code attempting to cause a DoS attack on the
browser.
Typically clobbering breaks the `nextSibling` property on an element so that it points to one of its child nodes. This
makes it impossible to walk the HTML tree without getting stuck in an infinite loop, which causes the browser to freeze.
+7
View File
@@ -0,0 +1,7 @@
@ngdoc error
@name ng:aobj
@fullName Invalid Argument
@description
The argument passed should be an object. Check the value that was passed to the function where
this error was thrown.
+41 -31
View File
@@ -232,27 +232,27 @@ than the hash fragment was changed.
### Example
```js
it('should show example', inject(
function($locationProvider) {
it('should show example', function() {
module(function($locationProvider) {
$locationProvider.html5Mode(false);
$locationProvider.hashPrefix('!');
},
function($location) {
});
inject(function($location) {
// open http://example.com/base/index.html#!/a
$location.absUrl() === 'http://example.com/base/index.html#!/a'
$location.path() === '/a'
expect($location.absUrl()).toBe('http://example.com/base/index.html#!/a');
expect($location.path()).toBe('/a');
$location.path('/foo')
$location.absUrl() === 'http://example.com/base/index.html#!/foo'
$location.path('/foo');
expect($location.absUrl()).toBe('http://example.com/base/index.html#!/foo');
$location.search() === {}
expect($location.search()).toEqual({});
$location.search({a: 'b', c: true});
$location.absUrl() === 'http://example.com/base/index.html#!/foo?a=b&c'
expect($location.absUrl()).toBe('http://example.com/base/index.html#!/foo?a=b&c');
$location.path('/new').search('x=y');
$location.absUrl() === 'http://example.com/base/index.html#!/new?x=y'
}
));
expect($location.absUrl()).toBe('http://example.com/base/index.html#!/new?x=y');
});
});
```
## HTML5 mode
@@ -274,40 +274,50 @@ and updates the url in a way that never performs a full page reload.
### Example
```js
it('should show example', inject(
function($locationProvider) {
it('should show example', function() {
module(function($locationProvider) {
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix('!');
},
function($location) {
});
inject(function($location) {
// in browser with HTML5 history support:
// open http://example.com/#!/a -> rewrite to http://example.com/a
// (replacing the http://example.com/#!/a history record)
$location.path() === '/a'
expect($location.path()).toBe('/a');
$location.path('/foo');
$location.absUrl() === 'http://example.com/foo'
expect($location.absUrl()).toBe('http://example.com/foo');
$location.search() === {}
expect($location.search()).toEqual({});
$location.search({a: 'b', c: true});
$location.absUrl() === 'http://example.com/foo?a=b&c'
expect($location.absUrl()).toBe('http://example.com/foo?a=b&c');
$location.path('/new').search('x=y');
$location.url() === 'new?x=y'
$location.absUrl() === 'http://example.com/new?x=y'
expect($location.url()).toBe('/new?x=y');
expect($location.absUrl()).toBe('http://example.com/new?x=y');
});
});
it('should show example (when browser doesn\'t support HTML5 mode', function() {
module(function($provide, $locationProvider) {
$locationProvider.html5Mode(true);
$locationProvider.hashPrefix('!');
$provide.value('$sniffer', {history: false});
});
inject(initBrowser({ url: 'http://example.com/new?x=y', basePath: '/' }),
function($location) {
// in browser without html5 history support:
// open http://example.com/new?x=y -> redirect to http://example.com/#!/new?x=y
// (again replacing the http://example.com/new?x=y history item)
$location.path() === '/new'
$location.search() === {x: 'y'}
expect($location.path()).toBe('/new');
expect($location.search()).toEqual({x: 'y'});
$location.path('/foo/bar');
$location.path() === '/foo/bar'
$location.url() === '/foo/bar?x=y'
$location.absUrl() === 'http://example.com/#!/foo/bar?x=y'
}
));
expect($location.path()).toBe('/foo/bar');
expect($location.url()).toBe('/foo/bar?x=y');
expect($location.absUrl()).toBe('http://example.com/#!/foo/bar?x=y');
});
});
```
### Fallback for legacy browsers
@@ -555,7 +565,7 @@ In these examples we use `<base href="/base/index.html" />`. The inputs represen
</example>
####Browser in HTML5 Fallback mode (Hashbang mode)
#### Browser in HTML5 Fallback mode (Hashbang mode)
<example module="hashbang-mode" name="location-hashbang-mode">
<file name="index.html">
<div ng-controller="LocationController">
+5 -5
View File
@@ -122,7 +122,7 @@ The same approach to animation can be used using JavaScript code (**jQuery is us
```js
myModule.animation('.repeated-item', function() {
return {
enter : function(element, done) {
enter: function(element, done) {
element.css('opacity',0);
jQuery(element).animate({
opacity: 1
@@ -137,7 +137,7 @@ myModule.animation('.repeated-item', function() {
}
}
},
leave : function(element, done) {
leave: function(element, done) {
element.css('opacity', 1);
jQuery(element).animate({
opacity: 0
@@ -152,7 +152,7 @@ myModule.animation('.repeated-item', function() {
}
}
},
move : function(element, done) {
move: function(element, done) {
element.css('opacity', 0);
jQuery(element).animate({
opacity: 1
@@ -169,8 +169,8 @@ myModule.animation('.repeated-item', function() {
},
// you can also capture these animation events
addClass : function(element, className, done) {},
removeClass : function(element, className, done) {}
addClass: function(element, className, done) {},
removeClass: function(element, className, done) {}
}
});
```
+1
View File
@@ -78,6 +78,7 @@ It's also possible to add components via {@link $compileProvider#component} in a
| link functions | Yes | No |
| multiElement | Yes | No |
| priority | Yes | No |
| replace | Yes (deprecated) | No |
| require | Yes | Yes |
| restrict | Yes | No (restricted to elements only) |
| scope | Yes (default: false) | No (scope is always isolate) |
+10 -4
View File
@@ -120,11 +120,13 @@ The other forms shown above are accepted for legacy reasons but we advise you to
### Directive types
`$compile` can match directives based on element names, attributes, class names, as well as comments.
`$compile` can match directives based on element names (E), attributes (A), class names (C),
and comments (M).
All of the Angular-provided directives match attribute name, tag name, comments, or class name.
The following demonstrates the various ways a directive (`myDir` in this case) can be referenced
from within a template:
The built-in the AngularJS directives show in their documentation page which type of matching they support.
The following demonstrates the various ways a directive that matches all 4 types
(`myDir` in this case) can be referenced from within a template.
```html
<my-dir></my-dir>
@@ -133,6 +135,10 @@ from within a template:
<span class="my-dir: exp;"></span>
```
A directive can specify which of the 4 matching types it supports in the
{@link ng.$compile#-restrict- `restrict`} property of the directive definition object.
The default is `EA`.
<div class="alert alert-success">
**Best Practice:** Prefer using directives via tag name and attributes over comment and class names.
Doing so generally makes it easier to determine what directives a given element matches.
+7 -7
View File
@@ -241,7 +241,7 @@ An expression that starts with `::` is considered a one-time expression. One-tim
will stop recalculating once they are stable, which happens after the first digest if the expression
result is a non-undefined value (see value stabilization algorithm below).
<example module="oneTimeBidingExampleApp" name="expression-one-time">
<example module="oneTimeBindingExampleApp" name="expression-one-time">
<file name="index.html">
<div ng-controller="EventController">
<button ng-click="clickMe($event)">Click Me</button>
@@ -250,7 +250,7 @@ result is a non-undefined value (see value stabilization algorithm below).
</div>
</file>
<file name="script.js">
angular.module('oneTimeBidingExampleApp', []).
angular.module('oneTimeBindingExampleApp', []).
controller('EventController', ['$scope', function($scope) {
var counter = 0;
var names = ['Igor', 'Misko', 'Chirayu', 'Lucas'];
@@ -265,24 +265,24 @@ result is a non-undefined value (see value stabilization algorithm below).
</file>
<file name="protractor.js" type="protractor">
it('should freeze binding after its value has stabilized', function() {
var oneTimeBiding = element(by.id('one-time-binding-example'));
var oneTimeBinding = element(by.id('one-time-binding-example'));
var normalBinding = element(by.id('normal-binding-example'));
expect(oneTimeBiding.getText()).toEqual('One time binding:');
expect(oneTimeBinding.getText()).toEqual('One time binding:');
expect(normalBinding.getText()).toEqual('Normal binding:');
element(by.buttonText('Click Me')).click();
expect(oneTimeBiding.getText()).toEqual('One time binding: Igor');
expect(oneTimeBinding.getText()).toEqual('One time binding: Igor');
expect(normalBinding.getText()).toEqual('Normal binding: Igor');
element(by.buttonText('Click Me')).click();
expect(oneTimeBiding.getText()).toEqual('One time binding: Igor');
expect(oneTimeBinding.getText()).toEqual('One time binding: Igor');
expect(normalBinding.getText()).toEqual('Normal binding: Misko');
element(by.buttonText('Click Me')).click();
element(by.buttonText('Click Me')).click();
expect(oneTimeBiding.getText()).toEqual('One time binding: Igor');
expect(oneTimeBinding.getText()).toEqual('One time binding: Igor');
expect(normalBinding.getText()).toEqual('Normal binding: Lucas');
});
</file>
+2 -1
View File
@@ -119,7 +119,8 @@ You can find a larger list of Angular external libraries at [ngmodules.org](http
### Books
* [AngularJS Directives](http://www.amazon.com/AngularJS-Directives-Alex-Vanston/dp/1783280336) by Alex Vanston
* [AngularJS Essentials (Free eBook)](https://www.packtpub.com/packt/free-ebook/angularjs-essentials) by Rodrigo Branas
* [AngularJS : Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
* [AngularJS in Action](https://www.manning.com/books/angularjs-in-action) by Lukas Ruebbelke
* [AngularJS: Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
* [AngularJS UI Development](http://www.amazon.com/AngularJS-UI-Development-Amit-Ghart-ebook/dp/B00OXVAK7A) by Amit Gharat and Matthias Nehlsen
* [AngularJS: Up and Running](http://www.amazon.com/AngularJS-Running-Enhanced-Productivity-Structured/dp/1491901942) by Brad Green and Shyam Seshadri
* [Developing an AngularJS Edge](http://www.amazon.com/Developing-AngularJS-Edge-Christopher-Hiller-ebook/dp/B00CJLFF8K) by Christopher Hiller
+2 -2
View File
@@ -28,8 +28,8 @@ for other directives to augment its behavior.
<form novalidate class="simple-form">
<label>Name: <input type="text" ng-model="user.name" /></label><br />
<label>E-mail: <input type="email" ng-model="user.email" /></label><br />
Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label>
<label><input type="radio" ng-model="user.gender" value="female" />female</label><br />
Best Editor: <label><input type="radio" ng-model="user.preference" value="vi" />vi</label>
<label><input type="radio" ng-model="user.preference" value="emacs" />emacs</label><br />
<input type="button" ng-click="reset()" value="Reset" />
<input type="submit" ng-click="update(user)" value="Save" />
</form>
+2 -2
View File
@@ -26,7 +26,7 @@ directive}. Additionally, you can use {@link guide/i18n#messageformat-extension
All localizable Angular components depend on locale-specific rule sets managed by the {@link
ng.$locale `$locale` service}.
There a few examples that showcase how to use Angular filters with various locale rule sets in the
There are a few examples that showcase how to use Angular filters with various locale rule sets in the
[`i18n/e2e` directory](https://github.com/angular/angular.js/tree/master/i18n/e2e) of the Angular
source code.
@@ -85,7 +85,7 @@ requires German locale, you would serve index_de-de.html which will look somethi
Both approaches described above require you to prepare different `index.html` pages or JavaScript
files for each locale that your app may use. You also need to configure your server to serve
the correct file that correspond to the desired locale.
the correct file that corresponds to the desired locale.
The second approach (including the locale JavaScript file in `index.html`) may be slower because
an extra script needs to be loaded.
+128 -12
View File
@@ -88,7 +88,9 @@ commits for more info.
- **jqLite** is more aligned to jQuery 3, which required the following changes
(see [details](guide/migration#migrate1.5to1.6-ng-misc-jqLite) below):
- Keys passed to `.data()` and `.css()` are now camelCased in the same as jQuery does it.
- Keys passed to `.data()` and `.css()` are now camelCased in the same way as the jQuery methods
do.
- Getting/setting boolean attributes no longer takes the corresponding properties into account.
- Setting boolean attributes to empty string no longer removes the attribute.
- Calling `.val()` on a multiple select will always return an array, even if no option is
selected.
@@ -416,7 +418,7 @@ will be removed in a future version, so we strongly recommend migrating your app
rely on it as soon as possible.
Initialization logic that relies on bindings being present should be put in the controller's
`$onInit()` method, which is guarranteed to always be called _after_ the bindings have been
`$onInit()` method, which is guaranteed to always be called _after_ the bindings have been
assigned.
Before:
@@ -501,7 +503,7 @@ running at `https://docs.angularjs.org` then the following will fail:
<link href="{{ 'http://mydomain.org/unsafe.css' }}" rel="stylesheet" />
```
By default, only URLs with the same domain and prototocl as the application document are considered
By default, only URLs with the same domain and protocol as the application document are considered
safe in the `RESOURCE_URL` context. To use URLs from other domains and/or protocols, you may either
whitelist them or wrap them into a trusted value by calling `$sce.trustAsResourceUrl(url)`.
@@ -880,6 +882,48 @@ var bgColor = elem.css('background-color');
var bgColor = elem.css('backgroundColor');
```
<hr />
<major />
**Due to [7ceb5f](https://github.com/angular/angular.js/commit/7ceb5f6fcc43d35d1b66c3151ce6a71c60309304)**,
getting/setting boolean attributes will no longer take the corresponding properties into account.
Previously, all boolean attributes were reflected into the corresponding property when calling a
setter and from the corresponding property when calling a getter, even on elements that don't treat
those attributes in a special way. Now Angular doesn't do it by itself, but relies on browsers to
know when to reflect the property. Note that this browser-level conversion differs between browsers;
if you need to dynamically change the state of an element, you should modify the property, not the
attribute. See https://jquery.com/upgrade-guide/1.9/#attr-versus-prop- for a more detailed
description about a related change in jQuery 1.9.
This change aligns jqLite with jQuery 3. To migrate the code follow the example below:
Before:
```css
/* CSS */
input[checked="checked"] { ... }
```
```js
// JS
elem1.attr('checked', 'checked');
elem2.attr('checked', false);
```
After:
```css
/* CSS */
input:checked { ... }
```
```js
// JS
elem1.prop('checked', true);
elem2.prop('checked', false);
```
<hr />
<major />
**Due to [3faf45](https://github.com/angular/angular.js/commit/3faf4505732758165083c9d21de71fa9b6983f4a)**,
@@ -1169,7 +1213,7 @@ with an unencoded `;` character.
Previously, in cases where `ngView` was loaded asynchronously, `$route` (and its dependencies) might
also have been instantiated asynchronously.
Although this is not expected to have unwanted side-effects in normal application bebavior, it may
Although this is not expected to have unwanted side-effects in normal application behavior, it may
affect your unit tests: When testing a module that (directly or indirectly) depends on `ngRoute`, a
request will be made for the default route's template. If not properly "trained", `$httpBackend`
will complain about this unexpected request. You can restore the previous behavior (and avoid
@@ -1410,9 +1454,8 @@ For more info on the topic, you can take a look at this
## Migrating from 1.3 to 1.4
Angular 1.4 fixes major animation issues and introduces a new API for `ngCookies`. Further, there
are changes to `ngMessages`, `$compile`, `ngRepeat`, `ngOptions `and some fixes to core filters:
`limitTo` and `filter`.
AngularJS 1.4 fixes major animation issues and introduces a new API for `ngCookies`. Further, there
are changes to `ngMessages`, `$compile`, `ngRepeat`, `ngOptions`, `ngPattern`, `pattern` and some fixes to core filters: `limitTo` and `filter`.
The reason for the ngAnimate refactor was to fix timing issues and to expose new APIs to allow
for developers to construct more versatile animations. We now have access to `$animateCss`
@@ -1425,9 +1468,9 @@ to render error messages with ngMessages that are listed with a directive such a
involves pulling error message data from a server and then displaying that data via the mechanics of ngMessages. Be
sure to read the breaking change involved with `ngMessagesInclude` to upgrade your template code.
Other changes, such as the ordering of elements with ngRepeat and ngOptions, may also affect the behavior of your
application. And be sure to also read up on the changes to `$cookies`. The migration jump from 1.3 to 1.4 should be
relatively straightforward otherwise.
Other changes, such as the ordering of elements with ngRepeat and ngOptions and the way ngPattern and pattern directives
validate the regex, may also affect the behavior of your application. And be sure to also read up on the changes to `$cookies`.
The migration jump from 1.3 to 1.4 should be relatively straightforward otherwise.
@@ -1531,7 +1574,7 @@ class based animations (animations triggered via ngClass) in order to ensure tha
### Forms (`ngMessages`, `ngOptions`, `select`)
### Forms (`ngMessages`, `ngOptions`, `select`, `ngPattern` and `pattern`)
#### ngMessages
The ngMessages module has also been subject to an internal refactor to allow it to be more flexible
@@ -1639,6 +1682,79 @@ ngModelCtrl.$formatters.push(function(value) {
});
```
#### ngPattern and pattern
Due to [0e001084](https://github.com/angular/angular.js/commit/0e001084ffff8674efad289d37cb16cc4e46b50a),
The `ngPattern` and `pattern` directives will validate the regex
against the `$viewValue` of `ngModel`, i.e. the value of the model
before the $parsers are applied. Previously, the `$modelValue`
(the result of the $parsers) was validated.
This fixes issues where `input[date]` and `input[number]` cannot
be validated because the `$viewValue` string is parsed into
`Date` and `Number` respectively (starting with Angular 1.3).
It also brings the directives in line with HTML5 constraint
validation, which validates against the input value.
This change is unlikely to cause applications to fail, because even
in Angular 1.2, the value that was validated by pattern could have
been manipulated by the $parsers, as all validation was done
inside this pipeline.
If you rely on the pattern being validated against the `$modelValue`,
you must create your own validator directive that overwrites
the built-in pattern validator:
```
.directive('patternModelOverwrite', function patternModelOverwriteDirective() {
return {
restrict: 'A',
require: '?ngModel',
priority: 1,
compile: function() {
var regexp, patternExp;
return {
pre: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
attr.$observe('pattern', function(regex) {
/**
* The built-in directive will call our overwritten validator
* (see below). We just need to update the regex.
* The preLink fn guaranetees our observer is called first.
*/
if (isString(regex) && regex.length > 0) {
regex = new RegExp('^' + regex + '$');
}
if (regex && !regex.test) {
//The built-in validator will throw at this point
return;
}
regexp = regex || undefined;
});
},
post: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
regexp, patternExp = attr.ngPattern || attr.pattern;
//The postLink fn guarantees we overwrite the built-in pattern validator
ctrl.$validators.pattern = function(value) {
return ctrl.$isEmpty(value) ||
isUndefined(regexp) ||
regexp.test(value);
};
}
};
}
};
});
```
### form
@@ -2605,7 +2721,7 @@ See [38deedd6](https://github.com/angular/angular.js/commit/38deedd6e3d806eb8262
### Interpolations inside DOM event handlers are now disallowed
DOM event handlers execute arbitrary Javascript code. Using an interpolation for such handlers
DOM event handlers execute arbitrary JavaScript code. Using an interpolation for such handlers
means that the interpolated value is a JS string that is evaluated. Storing or generating such
strings is error prone and leads to XSS vulnerabilities. On the other hand, `ngClick` and other
Angular specific event handlers evaluate Angular expressions in non-window (Scope) context which
+2 -2
View File
@@ -221,8 +221,8 @@ it('should clear messages after alert', function() {
notify('two');
notify('third');
expect(mock.alert.callCount).toEqual(2);
expect(mock.alert.mostRecentCall.args).toEqual(["more\ntwo\nthird"]);
expect(mock.alert.calls.count()).toEqual(2);
expect(mock.alert.calls.mostRecent().args).toEqual(["more\ntwo\nthird"]);
});
```
+3 -3
View File
@@ -140,13 +140,13 @@ tests once on Chrome run:
grunt test:unit
```
To run the tests on other browsers (Chrome, ChromeCanary, Firefox, Opera and Safari are pre-configured) use:
To run the tests on other browsers (Chrome, ChromeCanary, Firefox and Safari are pre-configured) use:
```shell
grunt test:unit --browsers=Opera,Firefox
grunt test:unit --browsers=Chrome,Firefox
```
Note there should be _no spaces between browsers_. `Opera, Firefox` is INVALID.
Note there should be _no spaces between browsers_. `Chrome, Firefox` is INVALID.
During development, however, it's more productive to continuously run unit tests every time the source or test files
change. To execute tests in this mode run:
+1 -1
View File
@@ -32,7 +32,7 @@ tutorials.
## Subscribe
* Subscribe to the [mailing list](http://groups.google.com/forum/?fromgroups#!forum/angular). Ask questions here!
* Follow us on [Twitter](https://twitter.com/intent/follow?original_referer=http%3A%2F%2Fangularjs.org%2F&region=follow_link&screen_name=angularjs&source=followbutton&variant=2.0)
* Follow us on [Twitter](https://twitter.com/intent/follow?original_referer=http%3A%2F%2Fangularjs.org%2F&region=follow_link&screen_name=angular&source=followbutton&variant=2.0)
* Add us to your circles on [Google+](https://plus.google.com/110323587230527980117/posts)
## Read more
+1 -1
View File
@@ -54,7 +54,7 @@ We will keep this in mind though, as we add more features.
So, now that we learned we should put everything in its own file, our `app/` directory will soon be
full with dozens of files and specs (remember we keep our unit test files next to the corresponding
source code files). What's more important, logically related files will not be grouped together; it
will be really difficult of locate all files related to a specific section of the application and
will be really difficult to locate all files related to a specific section of the application and
make a change or fix a bug.
So, what shall we do?
+1 -1
View File
@@ -194,7 +194,7 @@ angular.module('phonecatApp', [
```
Now, in addition to the core services and directives, we can also configure the `$route` service
(using it's provider) for our application. In order to be able to quickly locate the configuration
(using its provider) for our application. In order to be able to quickly locate the configuration
code, we put it into a separate file and used the `.config` suffix.
<br />
+4 -1
View File
@@ -173,7 +173,10 @@ angular.module('phoneList', ['core.phone']);
**`app/phone-detail/phone-detail.module.js`:**
```js
angular.module('phoneDetail', ['core.phone']);
angular.module('phoneDetail', [
'ngRoute',
'core.phone'
]);
```
<br />
+6 -21
View File
@@ -1,10 +1,8 @@
'use strict';
var gulp = require('gulp');
var log = require('gulp-util').log;
var concat = require('gulp-concat');
var eslint = require('gulp-eslint');
var bower = require('bower');
var Dgeni = require('dgeni');
var merge = require('event-stream').merge;
var path = require('canonical-path');
@@ -18,7 +16,6 @@ var rename = require('gulp-rename');
// See clean and bower for async tasks, and see assets and doc-gen for dependent tasks below
var outputFolder = '../build/docs';
var bowerFolder = 'bower_components';
var src = 'app/src/**/*.js';
var ignoredFiles = '!src/angular.bind.js';
@@ -57,8 +54,8 @@ var getMergedEslintConfig = function(filepath) {
var copyComponent = function(component, pattern, sourceFolder, packageFile) {
pattern = pattern || '/**/*';
sourceFolder = sourceFolder || bowerFolder;
packageFile = packageFile || 'bower.json';
sourceFolder = sourceFolder || '../node_modules';
packageFile = packageFile || 'package.json';
var version = require(path.resolve(sourceFolder, component, packageFile)).version;
return gulp
.src(sourceFolder + '/' + component + pattern)
@@ -66,18 +63,6 @@ var copyComponent = function(component, pattern, sourceFolder, packageFile) {
};
gulp.task('bower', function() {
var bowerTask = bower.commands.install();
bowerTask.on('log', function(result) {
log('bower:', result.id, result.data.endpoint.name);
});
bowerTask.on('error', function(error) {
log(error);
});
return bowerTask;
});
gulp.task('build-app', function() {
var file = 'docs.js';
var minFile = 'docs.min.js';
@@ -94,7 +79,7 @@ gulp.task('build-app', function() {
});
gulp.task('assets', ['bower'], function() {
gulp.task('assets', function() {
var JS_EXT = /\.js$/;
return merge(
gulp.src(['img/**/*']).pipe(gulp.dest(outputFolder + '/img')),
@@ -113,15 +98,15 @@ gulp.task('assets', ['bower'], function() {
})),
copyComponent('bootstrap', '/dist/**/*'),
copyComponent('open-sans-fontface'),
copyComponent('lunr.js', '/*.js'),
copyComponent('lunr', '/*.js'),
copyComponent('google-code-prettify'),
copyComponent('jquery', '/dist/*.js'),
copyComponent('marked', '/**/*.js', '../node_modules', 'package.json')
copyComponent('marked', '/**/*.js')
);
});
gulp.task('doc-gen', ['bower'], function() {
gulp.task('doc-gen', function() {
var dgeni = new Dgeni([require('./config')]);
return dgeni.generate().catch(function() {
process.exit(1);
+9 -4
View File
@@ -1,7 +1,7 @@
{
"name": "angularjs",
"license": "MIT",
"branchVersion": "^1.5.8",
"branchVersion": "^1.6.0",
"branchPattern": "1.6.*",
"distTag": "latest",
"repository": {
@@ -10,7 +10,7 @@
},
"engines": {
"node": "^6.9.1",
"yarn": "^0.17.9",
"yarn": ">=0.17.9",
"grunt": "^1.2.0"
},
"scripts": {
@@ -22,19 +22,21 @@
"devDependencies": {
"angular-benchpress": "0.x.x",
"benchmark": "1.x.x",
"bootstrap": "3.1.1",
"bower": "~1.3.9",
"browserstacktunnel-wrapper": "^1.4.2",
"canonical-path": "0.0.2",
"changez": "^2.1.1",
"changez-angular": "^2.1.0",
"changez-angular": "^2.1.3",
"cheerio": "^0.17.0",
"commitizen": "^2.3.0",
"cross-spawn": "^4.0.0",
"cz-conventional-changelog": "1.1.4",
"dgeni": "^0.4.0",
"dgeni-packages": "^0.16.0",
"dgeni-packages": "^0.16.4",
"event-stream": "~3.1.0",
"glob": "^6.0.1",
"google-code-prettify": "1.0.1",
"grunt": "^1.0.1",
"grunt-bump": "^0.8.0",
"grunt-cli": "^1.2.0",
@@ -57,6 +59,7 @@
"jasmine-core": "^2.4.0",
"jasmine-node": "^2.0.0",
"jasmine-reporters": "^2.2.0",
"jquery": "^3.1.1",
"karma": "^1.1.2",
"karma-browserstack-launcher": "^1.0.1",
"karma-chrome-launcher": "^1.0.1",
@@ -69,9 +72,11 @@
"load-grunt-tasks": "^3.5.0",
"lodash": "~2.4.1",
"log4js": "^0.6.27",
"lunr": "^0.7.2",
"marked": "~0.3.0",
"node-html-encoder": "0.0.2",
"npm-run": "^4.1.0",
"open-sans-fontface": "^1.4.0",
"promises-aplus-tests": "~2.1.0",
"protractor": "^4.0.10",
"q": "~1.0.0",
+1 -1
View File
@@ -12,7 +12,7 @@ set -xe
# This is the default set of browsers to use on the CI server unless overridden via env variable
if [[ -z "$BROWSERS" ]]
then
BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh"
BROWSERS="Chrome,Firefox,/Users/jenkins/bin/safari.sh"
fi
# CLEAN #
+4 -1
View File
@@ -15,6 +15,9 @@
"splice": false,
"push": false,
"toString": false,
"minErrConfig": false,
"errorHandlingConfig": false,
"isValidObjectMaxDepth": false,
"ngMinErr": false,
"_angular": false,
"angularModule": false,
@@ -150,7 +153,7 @@
/* apis.js */
"hashKey": false,
"HashMap": false,
"NgMap": false,
/* urlUtils.js */
"urlResolve": false,
+103 -30
View File
@@ -10,6 +10,9 @@
splice,
push,
toString,
minErrConfig,
errorHandlingConfig,
isValidObjectMaxDepth,
ngMinErr,
angularModule,
uid,
@@ -125,6 +128,50 @@ var VALIDITY_STATE_PROPERTY = 'validity';
var hasOwnProperty = Object.prototype.hasOwnProperty;
var minErrConfig = {
objectMaxDepth: 5
};
/**
* @ngdoc function
* @name angular.errorHandlingConfig
* @module ng
* @kind function
*
* @description
* Configure several aspects of error handling in AngularJS if used as a setter or return the
* current configuration if used as a getter. The following options are supported:
*
* - **objectMaxDepth**: The maximum depth to which objects are traversed when stringified for error messages.
*
* Omitted or undefined options will leave the corresponding configuration values unchanged.
*
* @param {Object=} config - The configuration object. May only contain the options that need to be
* updated. Supported keys:
*
* * `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a
* non-positive or non-numeric value, removes the max depth limit.
* Default: 5
*/
function errorHandlingConfig(config) {
if (isObject(config)) {
if (isDefined(config.objectMaxDepth)) {
minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
}
} else {
return minErrConfig;
}
}
/**
* @private
* @param {Number} maxDepth
* @return {boolean}
*/
function isValidObjectMaxDepth(maxDepth) {
return isNumber(maxDepth) && maxDepth > 0;
}
/**
* @ngdoc function
* @name angular.lowercase
@@ -847,9 +894,10 @@ function arrayRemove(array, value) {
</file>
</example>
*/
function copy(source, destination) {
function copy(source, destination, maxDepth) {
var stackSource = [];
var stackDest = [];
maxDepth = isValidObjectMaxDepth(maxDepth) ? maxDepth : NaN;
if (destination) {
if (isTypedArray(destination) || isArrayBuffer(destination)) {
@@ -872,35 +920,39 @@ function copy(source, destination) {
stackSource.push(source);
stackDest.push(destination);
return copyRecurse(source, destination);
return copyRecurse(source, destination, maxDepth);
}
return copyElement(source);
return copyElement(source, maxDepth);
function copyRecurse(source, destination) {
function copyRecurse(source, destination, maxDepth) {
maxDepth--;
if (maxDepth < 0) {
return '...';
}
var h = destination.$$hashKey;
var key;
if (isArray(source)) {
for (var i = 0, ii = source.length; i < ii; i++) {
destination.push(copyElement(source[i]));
destination.push(copyElement(source[i], maxDepth));
}
} 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]);
destination[key] = copyElement(source[key], maxDepth);
}
} 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]);
destination[key] = copyElement(source[key], maxDepth);
}
}
} else {
// Slowest path --- hasOwnProperty can't be called as a method
for (key in source) {
if (hasOwnProperty.call(source, key)) {
destination[key] = copyElement(source[key]);
destination[key] = copyElement(source[key], maxDepth);
}
}
}
@@ -908,7 +960,7 @@ function copy(source, destination) {
return destination;
}
function copyElement(source) {
function copyElement(source, maxDepth) {
// Simple values
if (!isObject(source)) {
return source;
@@ -937,7 +989,7 @@ function copy(source, destination) {
stackDest.push(destination);
return needsRecurse
? copyRecurse(source, destination)
? copyRecurse(source, destination, maxDepth)
: destination;
}
@@ -1479,30 +1531,51 @@ function getNgAttribute(element, ngAttr) {
}
function allowAutoBootstrap(document) {
if (!document.currentScript) {
var script = document.currentScript;
if (!script) {
// IE does not have `document.currentScript`
return true;
}
var src = document.currentScript.getAttribute('src');
var link = document.createElement('a');
link.href = src;
if (document.location.origin === link.origin) {
// Same-origin resources are always allowed, even for non-whitelisted schemes.
return true;
// If the `currentScript` property has been clobbered just return false, since this indicates a probable attack
if (!(script instanceof window.HTMLScriptElement || script instanceof window.SVGScriptElement)) {
return false;
}
// Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web.
// This is to prevent angular.js bundled with browser extensions from being used to bypass the
// content security policy in web pages and other browser extensions.
switch (link.protocol) {
case 'http:':
case 'https:':
case 'ftp:':
case 'blob:':
case 'file:':
case 'data:':
var attributes = script.attributes;
var srcs = [attributes.getNamedItem('src'), attributes.getNamedItem('href'), attributes.getNamedItem('xlink:href')];
return srcs.every(function(src) {
if (!src) {
return true;
default:
}
if (!src.value) {
return false;
}
}
var link = document.createElement('a');
link.href = src.value;
if (document.location.origin === link.origin) {
// Same-origin resources are always allowed, even for non-whitelisted schemes.
return true;
}
// Disabled bootstrapping unless angular.js was loaded from a known scheme used on the web.
// This is to prevent angular.js bundled with browser extensions from being used to bypass the
// content security policy in web pages and other browser extensions.
switch (link.protocol) {
case 'http:':
case 'https:':
case 'ftp:':
case 'blob:':
case 'file:':
case 'data:':
return true;
default:
return false;
}
});
}
// Cached as it has to run during loading so that document.currentScript is available.
@@ -1866,7 +1939,7 @@ function bindJQuery() {
extend(jQuery.fn, {
scope: JQLitePrototype.scope,
isolateScope: JQLitePrototype.isolateScope,
controller: JQLitePrototype.controller,
controller: /** @type {?} */ (JQLitePrototype).controller,
injector: JQLitePrototype.injector,
inheritedData: JQLitePrototype.inheritedData
});
+5 -3
View File
@@ -70,7 +70,6 @@
$$ForceReflowProvider,
$InterpolateProvider,
$IntervalProvider,
$$HashMapProvider,
$HttpProvider,
$HttpParamSerializerProvider,
$HttpParamSerializerJQLikeProvider,
@@ -79,6 +78,7 @@
$jsonpCallbacksProvider,
$LocationProvider,
$LogProvider,
$$MapProvider,
$ParseProvider,
$RootScopeProvider,
$QProvider,
@@ -126,6 +126,7 @@ var version = {
function publishExternalAPI(angular) {
extend(angular, {
'errorHandlingConfig': errorHandlingConfig,
'bootstrap': bootstrap,
'copy': copy,
'extend': extend,
@@ -260,9 +261,10 @@ function publishExternalAPI(angular) {
$window: $WindowProvider,
$$rAF: $$RAFProvider,
$$jqLite: $$jqLiteProvider,
$$HashMap: $$HashMapProvider,
$$Map: $$MapProvider,
$$cookieReader: $$CookieReaderProvider
});
}
]);
])
.info({ angularVersion: '"NG_VERSION_FULL"' });
}
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* @license AngularJS v"NG_VERSION_FULL"
* (c) 2010-2016 Google, Inc. http://angularjs.org
* (c) 2010-2017 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window) {
+55 -36
View File
@@ -1,6 +1,5 @@
'use strict';
/**
* Computes a hash of an 'obj'.
* Hash of a:
@@ -33,49 +32,69 @@ function hashKey(obj, nextUidFn) {
return key;
}
/**
* HashMap which can use objects as keys
*/
function HashMap(array, isolatedUid) {
if (isolatedUid) {
var uid = 0;
this.nextUid = function() {
return ++uid;
};
}
forEach(array, this.put, this);
// A minimal ES2015 Map implementation.
// Should be bug/feature equivalent to the native implementations of supported browsers
// (for the features required in Angular).
// See https://kangax.github.io/compat-table/es6/#test-Map
var nanKey = Object.create(null);
function NgMapShim() {
this._keys = [];
this._values = [];
this._lastKey = NaN;
this._lastIndex = -1;
}
HashMap.prototype = {
/**
* Store key value pair
* @param key key to store can be any type
* @param value value to store can be any type
*/
put: function(key, value) {
this[hashKey(key, this.nextUid)] = value;
NgMapShim.prototype = {
_idx: function(key) {
if (key === this._lastKey) {
return this._lastIndex;
}
this._lastKey = key;
this._lastIndex = this._keys.indexOf(key);
return this._lastIndex;
},
_transformKey: function(key) {
return isNumberNaN(key) ? nanKey : key;
},
/**
* @param key
* @returns {Object} the value for the key
*/
get: function(key) {
return this[hashKey(key, this.nextUid)];
key = this._transformKey(key);
var idx = this._idx(key);
if (idx !== -1) {
return this._values[idx];
}
},
set: function(key, value) {
key = this._transformKey(key);
var idx = this._idx(key);
if (idx === -1) {
idx = this._lastIndex = this._keys.length;
}
this._keys[idx] = key;
this._values[idx] = value;
/**
* Remove the key/value pair
* @param key
*/
remove: function(key) {
var value = this[key = hashKey(key, this.nextUid)];
delete this[key];
return value;
// Support: IE11
// Do not `return this` to simulate the partial IE11 implementation
},
delete: function(key) {
key = this._transformKey(key);
var idx = this._idx(key);
if (idx === -1) {
return false;
}
this._keys.splice(idx, 1);
this._values.splice(idx, 1);
this._lastKey = NaN;
this._lastIndex = -1;
return true;
}
};
var $$HashMapProvider = [/** @this */function() {
// For now, always use `NgMapShim`, even if `window.Map` is available. Some native implementations
// are still buggy (often in subtle ways) and can cause hard-to-debug failures. When native `Map`
// implementations get more stable, we can reconsider switching to `window.Map` (when available).
var NgMap = NgMapShim;
var $$MapProvider = [/** @this */function() {
this.$get = [function() {
return HashMap;
return NgMap;
}];
}];
+27 -7
View File
@@ -71,11 +71,7 @@ var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var $injectorMinErr = minErr('$injector');
function stringifyFn(fn) {
// Support: Chrome 50-51 only
// Creating a new string by adding `' '` at the end, to hack around some bug in Chrome v50/51
// (See https://github.com/angular/angular.js/issues/14487.)
// TODO (gkalpak): Remove workaround when Chrome v52 is released
return Function.prototype.toString.call(fn) + ' ';
return Function.prototype.toString.call(fn);
}
function extractArgs(fn) {
@@ -184,6 +180,28 @@ function annotate(fn, strictDi, name) {
* As an array of injection names, where the last item in the array is the function to call.
*/
/**
* @ngdoc property
* @name $injector#modules
* @type {Object}
* @description
* A hash containing all the modules that have been loaded into the
* $injector.
*
* You can use this property to find out information about a module via the
* {@link angular.Module#info `myModule.info(...)`} method.
*
* For example:
*
* ```
* var info = $injector.modules['ngAnimate'].info();
* ```
*
* **Do not use this property to attempt to modify the modules after the application
* has been bootstrapped.**
*/
/**
* @ngdoc method
* @name $injector#get
@@ -649,7 +667,7 @@ function createInjector(modulesToLoad, strictDi) {
var INSTANTIATING = {},
providerSuffix = 'Provider',
path = [],
loadedModules = new HashMap([], true),
loadedModules = new NgMap(),
providerCache = {
$provide: {
provider: supportObject(provider),
@@ -677,6 +695,7 @@ function createInjector(modulesToLoad, strictDi) {
instanceInjector = protoInstanceInjector;
providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
instanceInjector.modules = providerInjector.modules = createMap();
var runBlocks = loadModules(modulesToLoad);
instanceInjector = protoInstanceInjector.get('$injector');
instanceInjector.strictDi = strictDi;
@@ -757,7 +776,7 @@ function createInjector(modulesToLoad, strictDi) {
var runBlocks = [], moduleFn;
forEach(modulesToLoad, function(module) {
if (loadedModules.get(module)) return;
loadedModules.put(module, true);
loadedModules.set(module, true);
function runInvokeQueue(queue) {
var i, ii;
@@ -772,6 +791,7 @@ function createInjector(modulesToLoad, strictDi) {
try {
if (isString(module)) {
moduleFn = angularModule(module);
instanceInjector.modules[module] = moduleFn;
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
runInvokeQueue(moduleFn._invokeQueue);
runInvokeQueue(moduleFn._configBlocks);
+8 -5
View File
@@ -979,12 +979,15 @@ forEach({
after: function(element, newElement) {
var index = element, parent = element.parentNode;
newElement = new JQLite(newElement);
for (var i = 0, ii = newElement.length; i < ii; i++) {
var node = newElement[i];
parent.insertBefore(node, index.nextSibling);
index = node;
if (parent) {
newElement = new JQLite(newElement);
for (var i = 0, ii = newElement.length; i < ii; i++) {
var node = newElement[i];
parent.insertBefore(node, index.nextSibling);
index = node;
}
}
},
+42
View File
@@ -79,6 +79,9 @@ function setupModuleLoader(window) {
* @returns {angular.Module} new module with the {@link angular.Module} api.
*/
return function module(name, requires, configFn) {
var info = {};
var assertNotHasOwnProperty = function(name, context) {
if (name === 'hasOwnProperty') {
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
@@ -114,6 +117,45 @@ function setupModuleLoader(window) {
_configBlocks: configBlocks,
_runBlocks: runBlocks,
/**
* @ngdoc method
* @name angular.Module#info
* @module ng
*
* @param {Object=} info Information about the module
* @returns {Object|Module} The current info object for this module if called as a getter,
* or `this` if called as a setter.
*
* @description
* Read and write custom information about this module.
* For example you could put the version of the module in here.
*
* ```js
* angular.module('myModule', []).info({ version: '1.0.0' });
* ```
*
* The version could then be read back out by accessing the module elsewhere:
*
* ```
* var version = angular.module('myModule').info().version;
* ```
*
* You can also retrieve this information during runtime via the
* {@link $injector#modules `$injector.modules`} property:
*
* ```js
* var version = $injector.modules['myModule'].info().version;
* ```
*/
info: function(value) {
if (isDefined(value)) {
if (!isObject(value)) throw ngMinErr('aobj', 'Argument \'{0}\' must be an object', 'value');
info = value;
return this;
}
return info;
},
/**
* @ngdoc property
* @name angular.Module#requires
+5 -2
View File
@@ -1,8 +1,11 @@
/**
* @license AngularJS v"NG_VERSION_FULL"
* (c) 2010-2016 Google, Inc. http://angularjs.org
* (c) 2010-2017 Google, Inc. http://angularjs.org
* License: MIT
*/
'use strict';
(function() {
function isFunction(value) {return typeof value === 'function';};
function isFunction(value) {return typeof value === 'function';}
function isDefined(value) {return typeof value !== 'undefined';}
function isObject(value) {return value !== null && typeof value === 'object';}
+10 -12
View File
@@ -33,20 +33,19 @@
function minErr(module, ErrorConstructor) {
ErrorConstructor = ErrorConstructor || Error;
return function() {
var SKIP_INDEXES = 2;
var templateArgs = arguments,
code = templateArgs[0],
var code = arguments[0],
template = arguments[1],
message = '[' + (module ? module + ':' : '') + code + '] ',
template = templateArgs[1],
templateArgs = sliceArgs(arguments, 2).map(function(arg) {
return toDebugString(arg, minErrConfig.objectMaxDepth);
}),
paramPrefix, i;
message += template.replace(/\{\d+\}/g, function(match) {
var index = +match.slice(1, -1),
shiftedIndex = index + SKIP_INDEXES;
var index = +match.slice(1, -1);
if (shiftedIndex < templateArgs.length) {
return toDebugString(templateArgs[shiftedIndex]);
if (index < templateArgs.length) {
return templateArgs[index];
}
return match;
@@ -55,9 +54,8 @@ function minErr(module, ErrorConstructor) {
message += '\nhttp://errors.angularjs.org/"NG_VERSION_FULL"/' +
(module ? module + '/' : '') + code;
for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
encodeURIComponent(toDebugString(templateArgs[i]));
for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
}
return new ErrorConstructor(message);
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* @license AngularJS v"NG_VERSION_FULL"
* (c) 2010-2016 Google, Inc. http://angularjs.org
* (c) 2010-2017 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular) {
+11 -9
View File
@@ -60,7 +60,7 @@ var $$CoreAnimateJsProvider = /** @this */ function() {
// this is prefixed with Core since it conflicts with
// the animateQueueProvider defined in ngAnimate/animateQueue.js
var $$CoreAnimateQueueProvider = /** @this */ function() {
var postDigestQueue = new HashMap();
var postDigestQueue = new NgMap();
var postDigestElements = [];
this.$get = ['$$AnimateRunner', '$rootScope',
@@ -139,7 +139,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() {
jqLiteRemoveClass(elm, toRemove);
}
});
postDigestQueue.remove(element);
postDigestQueue.delete(element);
}
});
postDigestElements.length = 0;
@@ -154,7 +154,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() {
if (classesAdded || classesRemoved) {
postDigestQueue.put(element, data);
postDigestQueue.set(element, data);
postDigestElements.push(element);
if (postDigestElements.length === 1) {
@@ -179,6 +179,7 @@ var $$CoreAnimateQueueProvider = /** @this */ function() {
*/
var $AnimateProvider = ['$provide', /** @this */ function($provide) {
var provider = this;
var classNameFilter = null;
this.$$registeredAnimations = Object.create(null);
@@ -247,15 +248,16 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
*/
this.classNameFilter = function(expression) {
if (arguments.length === 1) {
this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
if (this.$$classNameFilter) {
var reservedRegex = new RegExp('(\\s+|\\/)' + NG_ANIMATE_CLASSNAME + '(\\s+|\\/)');
if (reservedRegex.test(this.$$classNameFilter.toString())) {
throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
classNameFilter = (expression instanceof RegExp) ? expression : null;
if (classNameFilter) {
var reservedRegex = new RegExp('[(\\s|\\/)]' + NG_ANIMATE_CLASSNAME + '[(\\s|\\/)]');
if (reservedRegex.test(classNameFilter.toString())) {
classNameFilter = null;
throw $animateMinErr('nongcls', '$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
}
}
}
return this.$$classNameFilter;
return classNameFilter;
};
this.$get = ['$$animateQueue', function($$animateQueue) {
+11 -10
View File
@@ -96,7 +96,6 @@ function Browser(window, document, $log, $sniffer) {
};
cacheState();
lastHistoryState = cachedState;
/**
* @name $browser#url
@@ -150,8 +149,6 @@ function Browser(window, document, $log, $sniffer) {
if ($sniffer.history && (!sameBase || !sameState)) {
history[replace ? 'replaceState' : 'pushState'](state, '', url);
cacheState();
// Do the assignment again so that those two variables are referentially identical.
lastHistoryState = cachedState;
} else {
if (!sameBase) {
pendingLocation = url;
@@ -200,8 +197,7 @@ function Browser(window, document, $log, $sniffer) {
function cacheStateAndFireUrlChange() {
pendingLocation = null;
cacheState();
fireUrlChange();
fireStateOrUrlChange();
}
// This variable should be used *only* inside the cacheState function.
@@ -215,11 +211,16 @@ function Browser(window, document, $log, $sniffer) {
if (equals(cachedState, lastCachedState)) {
cachedState = lastCachedState;
}
lastCachedState = cachedState;
lastHistoryState = cachedState;
}
function fireUrlChange() {
if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
function fireStateOrUrlChange() {
var prevLastHistoryState = lastHistoryState;
cacheState();
if (lastBrowserUrl === self.url() && prevLastHistoryState === cachedState) {
return;
}
@@ -254,8 +255,8 @@ function Browser(window, document, $log, $sniffer) {
self.onUrlChange = function(callback) {
// TODO(vojta): refactor to use node's syntax for events
if (!urlChangeInit) {
// We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
// don't fire popstate when user change the address bar and don't fire hashchange when url
// We listen on both (hashchange/popstate) when available, as some browsers don't
// fire popstate when user changes the address bar and don't fire hashchange when url
// changed by push/replaceState
// html5 history api - popstate event
@@ -285,7 +286,7 @@ function Browser(window, document, $log, $sniffer) {
* Needs to be exported to be able to check for changes that have been done in sync,
* as hashchange/popstate events fire in async.
*/
self.$$checkUrlChange = fireUrlChange;
self.$$checkUrlChange = fireStateOrUrlChange;
//////////////////////////////////////////////////////////////
// Misc API
+10 -7
View File
@@ -128,7 +128,8 @@
* * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
* `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
* object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
* component such as cloning the bound value to prevent accidental mutation of the outer value.
* component such as cloning the bound value to prevent accidental mutation of the outer value. Note that this will
* also be called when your bindings are initialized.
* * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on
* changes. Any actions that you wish to take in response to the changes that you detect must be
* invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook
@@ -276,10 +277,12 @@
* the directive's element. If multiple directives on the same element request a new scope,
* only one new scope is created.
*
* * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
* 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
* scope. This is useful when creating reusable components, which should not accidentally read or modify
* data in the parent scope.
* * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's template.
* The 'isolate' scope differs from normal scope in that it does not prototypically
* inherit from its parent scope. This is useful when creating reusable components, which should not
* accidentally read or modify data in the parent scope. Note that an isolate scope
* directive without a `template` or `templateUrl` will not apply the isolate scope
* to its children elements.
*
* The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
* directive's element. These local properties are useful for aliasing values for templates. The keys in
@@ -983,7 +986,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var bindingCache = createMap();
function parseIsolateBindings(scope, directiveName, isController) {
var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*([\w$]*)\s*$/;
var bindings = createMap();
@@ -3155,7 +3158,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (error instanceof Error) {
$exceptionHandler(error);
}
}).catch(noop);
});
return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
var childBoundTranscludeFn = boundTranscludeFn;
+9 -1
View File
@@ -14,6 +14,14 @@ function $$CookieReader($document) {
var lastCookies = {};
var lastCookieString = '';
function safeGetCookie(rawDocument) {
try {
return rawDocument.cookie || '';
} catch (e) {
return '';
}
}
function safeDecodeURIComponent(str) {
try {
return decodeURIComponent(str);
@@ -24,7 +32,7 @@ function $$CookieReader($document) {
return function() {
var cookieArray, cookie, i, index, name;
var currentCookieString = rawDocument.cookie || '';
var currentCookieString = safeGetCookie(rawDocument);
if (currentCookieString !== lastCookieString) {
lastCookieString = currentCookieString;
+2 -1
View File
@@ -159,7 +159,8 @@
*
* @description
*
* This directive sets the `disabled` attribute on the element if the
* This directive sets the `disabled` attribute on the element (typically a form control,
* e.g. `input`, `button`, `select` etc.) if the
* {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
*
* A special directive is necessary because we cannot use interpolation inside the `disabled`
+18 -3
View File
@@ -1565,15 +1565,27 @@ function isValidForStep(viewValue, stepBase, step) {
// and `viewValue` is expected to be a valid stringified number.
var value = Number(viewValue);
var isNonIntegerValue = !isNumberInteger(value);
var isNonIntegerStepBase = !isNumberInteger(stepBase);
var isNonIntegerStep = !isNumberInteger(step);
// Due to limitations in Floating Point Arithmetic (e.g. `0.3 - 0.2 !== 0.1` or
// `0.5 % 0.1 !== 0`), we need to convert all numbers to integers.
if (!isNumberInteger(value) || !isNumberInteger(stepBase) || !isNumberInteger(step)) {
var decimalCount = Math.max(countDecimals(value), countDecimals(stepBase), countDecimals(step));
if (isNonIntegerValue || isNonIntegerStepBase || isNonIntegerStep) {
var valueDecimals = isNonIntegerValue ? countDecimals(value) : 0;
var stepBaseDecimals = isNonIntegerStepBase ? countDecimals(stepBase) : 0;
var stepDecimals = isNonIntegerStep ? countDecimals(step) : 0;
var decimalCount = Math.max(valueDecimals, stepBaseDecimals, stepDecimals);
var multiplier = Math.pow(10, decimalCount);
value = value * multiplier;
stepBase = stepBase * multiplier;
step = step * multiplier;
if (isNonIntegerValue) value = Math.round(value);
if (isNonIntegerStepBase) stepBase = Math.round(stepBase);
if (isNonIntegerStep) step = Math.round(step);
}
return (value - stepBase) % step === 0;
@@ -2130,7 +2142,10 @@ var ngValueDirective = function() {
* makes it possible to use ngValue as a sort of one-way bind.
*/
function updateElementValue(element, attr, value) {
element.prop('value', value);
// Support: IE9 only
// In IE9 values are converted to string (e.g. `input.value = null` results in `input.value === 'null'`).
var propValue = isDefined(value) ? value : (msie === 9) ? '' : null;
element.prop('value', propValue);
attr.$set('value', value);
}
+144 -95
View File
@@ -8,51 +8,71 @@
function classDirective(name, selector) {
name = 'ngClass' + name;
return ['$animate', function($animate) {
var indexWatchExpression;
return ['$parse', function($parse) {
return {
restrict: 'AC',
link: function(scope, element, attr) {
var oldVal;
var expression = attr[name].trim();
var isOneTime = (expression.charAt(0) === ':') && (expression.charAt(1) === ':');
scope.$watch(attr[name], ngClassWatchAction, true);
var watchInterceptor = isOneTime ? toFlatValue : toClassString;
var watchExpression = $parse(expression, watchInterceptor);
var watchAction = isOneTime ? ngClassOneTimeWatchAction : ngClassWatchAction;
attr.$observe('class', function(value) {
ngClassWatchAction(scope.$eval(attr[name]));
});
var classCounts = element.data('$classCounts');
var oldModulo = true;
var oldClassString;
if (name !== 'ngClass') {
scope.$watch('$index', function($index, old$index) {
/* eslint-disable no-bitwise */
var mod = $index & 1;
if (mod !== (old$index & 1)) {
var classes = arrayClasses(scope.$eval(attr[name]));
if (mod === selector) {
addClasses(classes);
} else {
removeClasses(classes);
}
}
/* eslint-enable */
});
}
function addClasses(classes) {
var newClasses = digestClassCounts(classes, 1);
attr.$addClass(newClasses);
}
function removeClasses(classes) {
var newClasses = digestClassCounts(classes, -1);
attr.$removeClass(newClasses);
}
function digestClassCounts(classes, count) {
if (!classCounts) {
// Use createMap() to prevent class assumptions involving property
// names in Object.prototype
var classCounts = element.data('$classCounts') || createMap();
classCounts = createMap();
element.data('$classCounts', classCounts);
}
if (name !== 'ngClass') {
if (!indexWatchExpression) {
indexWatchExpression = $parse('$index', function moduloTwo($index) {
// eslint-disable-next-line no-bitwise
return $index & 1;
});
}
scope.$watch(indexWatchExpression, ngClassIndexWatchAction);
}
scope.$watch(watchExpression, watchAction, isOneTime);
function addClasses(classString) {
classString = digestClassCounts(split(classString), 1);
attr.$addClass(classString);
}
function removeClasses(classString) {
classString = digestClassCounts(split(classString), -1);
attr.$removeClass(classString);
}
function updateClasses(oldClassString, newClassString) {
var oldClassArray = split(oldClassString);
var newClassArray = split(newClassString);
var toRemoveArray = arrayDifference(oldClassArray, newClassArray);
var toAddArray = arrayDifference(newClassArray, oldClassArray);
var toRemoveString = digestClassCounts(toRemoveArray, -1);
var toAddString = digestClassCounts(toAddArray, 1);
attr.$addClass(toAddString);
attr.$removeClass(toRemoveString);
}
function digestClassCounts(classArray, count) {
var classesToUpdate = [];
forEach(classes, function(className) {
forEach(classArray, function(className) {
if (count > 0 || classCounts[className]) {
classCounts[className] = (classCounts[className] || 0) + count;
if (classCounts[className] === +(count > 0)) {
@@ -60,77 +80,106 @@ function classDirective(name, selector) {
}
}
});
element.data('$classCounts', classCounts);
return classesToUpdate.join(' ');
}
function updateClasses(oldClasses, newClasses) {
var toAdd = arrayDifference(newClasses, oldClasses);
var toRemove = arrayDifference(oldClasses, newClasses);
toAdd = digestClassCounts(toAdd, 1);
toRemove = digestClassCounts(toRemove, -1);
if (toAdd && toAdd.length) {
$animate.addClass(element, toAdd);
function ngClassIndexWatchAction(newModulo) {
// This watch-action should run before the `ngClass[OneTime]WatchAction()`, thus it
// adds/removes `oldClassString`. If the `ngClass` expression has changed as well, the
// `ngClass[OneTime]WatchAction()` will update the classes.
if (newModulo === selector) {
addClasses(oldClassString);
} else {
removeClasses(oldClassString);
}
if (toRemove && toRemove.length) {
$animate.removeClass(element, toRemove);
oldModulo = newModulo;
}
function ngClassOneTimeWatchAction(newClassValue) {
var newClassString = toClassString(newClassValue);
if (newClassString !== oldClassString) {
ngClassWatchAction(newClassString);
}
}
function ngClassWatchAction(newVal) {
// eslint-disable-next-line no-bitwise
if (selector === true || (scope.$index & 1) === selector) {
var newClasses = arrayClasses(newVal || []);
if (!oldVal) {
addClasses(newClasses);
} else if (!equals(newVal,oldVal)) {
var oldClasses = arrayClasses(oldVal);
updateClasses(oldClasses, newClasses);
}
}
if (isArray(newVal)) {
oldVal = newVal.map(function(v) { return shallowCopy(v); });
} else {
oldVal = shallowCopy(newVal);
function ngClassWatchAction(newClassString) {
if (oldModulo === selector) {
updateClasses(oldClassString, newClassString);
}
oldClassString = newClassString;
}
}
};
function arrayDifference(tokens1, tokens2) {
var values = [];
outer:
for (var i = 0; i < tokens1.length; i++) {
var token = tokens1[i];
for (var j = 0; j < tokens2.length; j++) {
if (token === tokens2[j]) continue outer;
}
values.push(token);
}
return values;
}
function arrayClasses(classVal) {
var classes = [];
if (isArray(classVal)) {
forEach(classVal, function(v) {
classes = classes.concat(arrayClasses(v));
});
return classes;
} else if (isString(classVal)) {
return classVal.split(' ');
} else if (isObject(classVal)) {
forEach(classVal, function(v, k) {
if (v) {
classes = classes.concat(k.split(' '));
}
});
return classes;
}
return classVal;
}
}];
// Helpers
function arrayDifference(tokens1, tokens2) {
if (!tokens1 || !tokens1.length) return [];
if (!tokens2 || !tokens2.length) return tokens1;
var values = [];
outer:
for (var i = 0; i < tokens1.length; i++) {
var token = tokens1[i];
for (var j = 0; j < tokens2.length; j++) {
if (token === tokens2[j]) continue outer;
}
values.push(token);
}
return values;
}
function split(classString) {
return classString && classString.split(' ');
}
function toClassString(classValue) {
var classString = classValue;
if (isArray(classValue)) {
classString = classValue.map(toClassString).join(' ');
} else if (isObject(classValue)) {
classString = Object.keys(classValue).
filter(function(key) { return classValue[key]; }).
join(' ');
}
return classString;
}
function toFlatValue(classValue) {
var flatValue = classValue;
if (isArray(classValue)) {
flatValue = classValue.map(toFlatValue);
} else if (isObject(classValue)) {
var hasUndefined = false;
flatValue = Object.keys(classValue).filter(function(key) {
var value = classValue[key];
if (!hasUndefined && isUndefined(value)) {
hasUndefined = true;
}
return value;
});
if (hasUndefined) {
// Prevent the `oneTimeLiteralWatchInterceptor` from unregistering
// the watcher, by including at least one `undefined` value.
flatValue.push(undefined);
}
}
return flatValue;
}
}
/**
+6 -6
View File
@@ -53,15 +53,15 @@ forEach(
return {
restrict: 'A',
compile: function($element, attr) {
// We expose the powerful $event object on the scope that provides access to the Window,
// etc. that isn't protected by the fast paths in $parse. We explicitly request better
// checks at the cost of speed since event handler expressions are not executed as
// frequently as regular change detection.
var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
// NOTE:
// We expose the powerful `$event` object on the scope that provides access to the Window,
// etc. This is OK, because expressions are not sandboxed any more (and the expression
// sandbox was never meant to be a security feature anyway).
var fn = $parse(attr[directiveName]);
return function ngEventHandler(scope, element) {
element.on(eventName, function(event) {
var callback = function() {
fn(scope, {$event:event});
fn(scope, {$event: event});
};
if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
scope.$evalAsync(callback);
+66 -17
View File
@@ -31,32 +31,57 @@ var ngModelMinErr = minErr('ngModel');
* @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
its return value through to the next. The last return value is forwarded to the
{@link ngModel.NgModelController#$validators `$validators`} collection.
* the control updates the ngModelController with a new {@link ngModel.NgModelController#$viewValue
`$viewValue`} from the DOM, usually via user input.
See {@link ngModel.NgModelController#$setViewValue `$setViewValue()`} for a detailed lifecycle explanation.
Note that the `$parsers` are not called when the bound ngModel expression changes programmatically.
Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
`$viewValue`}.
The functions are called in array order, each passing
its return value through to the next. The last return value is forwarded to the
{@link ngModel.NgModelController#$validators `$validators`} collection.
Returning `undefined` from a parser means a parse error occurred. In that case,
no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
is set to `true`. The parse error is stored in `ngModel.$error.parse`.
Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
`$viewValue`}.
Returning `undefined` from a parser means a parse error occurred. In that case,
no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
is set to `true`. The parse error is stored in `ngModel.$error.parse`.
This simple example shows a parser that would convert text input value to lowercase:
* ```js
* function parse(value) {
* if (value) {
* return value.toLowerCase();
* }
* }
* ngModelController.$parsers.push(parse);
* ```
*
* @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
the model value changes. The functions are called in reverse array order, each passing the value through to the
next. The last return value is used as the actual DOM value.
Used to format / convert values for display in the control.
the bound ngModel expression changes programmatically. The `$formatters` are not called when the
value of the control is changed by user interaction.
Formatters are used to format / convert the {@link ngModel.NgModelController#$modelValue
`$modelValue`} for display in the control.
The functions are called in reverse array order, each passing the value through to the
next. The last return value is used as the actual DOM value.
This simple example shows a formatter that would convert the model value to uppercase:
* ```js
* function formatter(value) {
* function format(value) {
* if (value) {
* return value.toUpperCase();
* }
* }
* ngModel.$formatters.push(formatter);
* ngModel.$formatters.push(format);
* ```
*
* @property {Object.<string, function>} $validators A collection of validators that are applied
@@ -764,9 +789,10 @@ NgModelController.prototype = {
*
* When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
* and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
* value sent directly for processing, finally to be applied to `$modelValue` and then the
* **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
* in the `$viewChangeListeners` list, are called.
* value is sent directly for processing through the `$parsers` pipeline. After this, the `$validators` and
* `$asyncValidators` are called and the value is applied to `$modelValue`.
* Finally, the value is set to the **expression** specified in the `ng-model` attribute and
* all the registered change listeners, in the `$viewChangeListeners` list are called.
*
* In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
* and the `default` trigger is not listed, all those actions will remain pending until one of the
@@ -829,6 +855,29 @@ NgModelController.prototype = {
that.$commitViewValue();
});
}
},
/**
* @ngdoc method
*
* @name ngModel.NgModelController#$overrideModelOptions
*
* @description
*
* Override the current model options settings programmatically.
*
* The previous `ModelOptions` value will not be modified. Instead, a
* new `ModelOptions` object will inherit from the previous one overriding
* or inheriting settings that are defined in the given parameter.
*
* See {@link ngModelOptions} for information about what options can be specified
* and how model option inheritance works.
*
* @param {Object} options a hash of settings to override the previous options
*
*/
$overrideModelOptions: function(options) {
this.$options = this.$options.createChild(options);
}
};
+17 -9
View File
@@ -331,19 +331,27 @@ defaultModelOptions = new ModelOptions({
*
*/
var ngModelOptionsDirective = function() {
NgModelOptionsController.$inject = ['$attrs', '$scope'];
function NgModelOptionsController($attrs, $scope) {
this.$$attrs = $attrs;
this.$$scope = $scope;
}
NgModelOptionsController.prototype = {
$onInit: function() {
var parentOptions = this.parentCtrl ? this.parentCtrl.$options : defaultModelOptions;
var modelOptionsDefinition = this.$$scope.$eval(this.$$attrs.ngModelOptions);
this.$options = parentOptions.createChild(modelOptionsDefinition);
}
};
return {
restrict: 'A',
// ngModelOptions needs to run before ngModel and input directives
priority: 10,
require: ['ngModelOptions', '?^^ngModelOptions'],
controller: function NgModelOptionsController() {},
link: {
pre: function ngModelOptionsPreLinkFn(scope, element, attrs, ctrls) {
var optionsCtrl = ctrls[0];
var parentOptions = ctrls[1] ? ctrls[1].$options : defaultModelOptions;
optionsCtrl.$options = parentOptions.createChild(scope.$eval(attrs.ngModelOptions));
}
}
require: {parentCtrl: '?^^ngModelOptions'},
bindToController: true,
controller: NgModelOptionsController
};
};
+27 -17
View File
@@ -111,13 +111,8 @@ var ngOptionsMinErr = minErr('ngOptions');
* 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.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required The control is considered valid only if value is 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` when you want to data-bind to the `required` attribute.
* @param {comprehension_expression=} ngOptions in one of the following forms:
* @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {comprehension_expression} ngOptions in one of the following forms:
*
* * for array data sources:
* * `label` **`for`** `value` **`in`** `array`
@@ -156,6 +151,13 @@ var ngOptionsMinErr = minErr('ngOptions');
* used to identify the objects in the array. The `trackexpr` will most likely refer to the
* `value` variable (e.g. `value.propertyName`). With this the selection is preserved
* even when the options are recreated (e.g. reloaded from the server).
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required The control is considered valid only if value is 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` when you want to data-bind to the `required` attribute.
* @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
* {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
*
* @example
<example module="selectExample" name="select">
@@ -505,17 +507,17 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
} else {
selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
options.items.forEach(function(option) {
option.element.selected = false;
});
selectCtrl.writeValue = function writeNgOptionsMultiple(values) {
// Only set `<option>.selected` if necessary, in order to prevent some browsers from
// scrolling to `<option>` elements that are outside the `<select>` element's viewport.
if (value) {
value.forEach(function(item) {
var option = options.getOptionFromViewValue(item);
if (option) option.element.selected = true;
});
}
var selectedOptions = values && values.map(getAndUpdateSelectedOption) || [];
options.items.forEach(function(option) {
if (option.element.selected && !includes(selectedOptions, option)) {
option.element.selected = false;
}
});
};
@@ -605,6 +607,14 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
updateOptionElement(option, optionElement);
}
function getAndUpdateSelectedOption(viewValue) {
var option = options.getOptionFromViewValue(viewValue);
var element = option && option.element;
if (element && !element.selected) element.selected = true;
return option;
}
function updateOptionElement(option, element) {
option.element = element;
+1
View File
@@ -6,6 +6,7 @@
* @ngdoc directive
* @name ngRepeat
* @multiElement
* @restrict A
*
* @description
* The `ngRepeat` directive instantiates a template once per item from a collection. Each template
+224 -159
View File
@@ -8,11 +8,13 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* @multiElement
*
* @description
* The `ngShow` directive shows or hides the given HTML element based on the expression
* provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
* the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
* in AngularJS and sets the display style to none (using an !important flag).
* For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
* The `ngShow` directive shows or hides the given HTML element based on the expression provided to
* the `ngShow` attribute.
*
* The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
* The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
* `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
* {@link ng.directive:ngCsp ngCsp}).
*
* ```html
* <!-- when $scope.myValue is truthy (element is visible) -->
@@ -22,31 +24,32 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* <div ng-show="myValue" class="ng-hide"></div>
* ```
*
* When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
* attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
* from the element causing the element not to appear hidden.
* When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added
* to the class attribute on the element causing it to become hidden. When truthy, the `.ng-hide`
* CSS class is removed from the element causing the element not to appear hidden.
*
* ## Why is !important used?
* ## Why is `!important` used?
*
* You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
* can be easily overridden by heavier selectors. For example, something as simple
* as changing the display style on a HTML list item would make hidden elements appear visible.
* This also becomes a bigger issue when dealing with CSS frameworks.
* You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
* `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
* simple as changing the display style on a HTML list item would make hidden elements appear
* visible. This also becomes a bigger issue when dealing with CSS frameworks.
*
* By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
* specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
* styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
* By using `!important`, the show and hide behavior will work as expected despite any clash between
* CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
* developer chooses to override the styling to change how to hide an element then it is just a
* matter of using `!important` in their own CSS code.
*
* ### Overriding `.ng-hide`
*
* By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
* the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
* class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
* with extra animation classes that can be added.
* By default, the `.ng-hide` class will style the element with `display: none !important`. If you
* wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
* the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
* `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
*
* ```css
* .ng-hide:not(.ng-hide-animate) {
* /&#42; this is just another form of hiding an element &#42;/
* /&#42; These are just alternative ways of hiding an element &#42;/
* display: block!important;
* position: absolute;
* top: -9999px;
@@ -54,29 +57,20 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* }
* ```
*
* By default you don't need to override in CSS anything and the animations will work around the display style.
* By default you don't need to override anything in CSS and the animations will work around the
* display style.
*
* ## A note about animations with `ngShow`
*
* Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
* is true and false. This system works like the animation system present with ngClass except that
* you must also include the !important flag to override the display property
* so that you can perform an animation when the element is hidden during the time of the animation.
* Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
* directive expression is true and false. This system works like the animation system present with
* `ngClass` except that you must also include the `!important` flag to override the display
* property so that the elements are not actually hidden during the animation.
*
* ```css
* //
* //a working example can be found at the bottom of this page
* //
* /&#42; A working example can be found at the bottom of this page. &#42;/
* .my-element.ng-hide-add, .my-element.ng-hide-remove {
* /&#42; this is required as of 1.3x to properly
* apply all styling in a show/hide animation &#42;/
* transition: 0s linear all;
* }
*
* .my-element.ng-hide-add-active,
* .my-element.ng-hide-remove-active {
* /&#42; the transition is defined in the active class &#42;/
* transition: 1s linear all;
* transition: all 0.5s linear;
* }
*
* .my-element.ng-hide-add { ... }
@@ -85,76 +79,108 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
* ```
*
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
* to block during animation states - ngAnimate will automatically handle the style toggling for you.
*
* @animations
* | Animation | Occurs |
* |----------------------------------|-------------------------------------|
* | {@link $animate#addClass addClass} `.ng-hide` | after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden |
* | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible |
* | Animation | Occurs |
* |-----------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
* | {@link $animate#addClass addClass} `.ng-hide` | After the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden. |
* | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngShow` expression evaluates to a truthy value and just before contents are set to visible. |
*
* @element ANY
* @param {expression} ngShow If the {@link guide/expression expression} is truthy
* then the element is shown or hidden respectively.
* @param {expression} ngShow If the {@link guide/expression expression} is truthy/falsy then the
* element is shown/hidden respectively.
*
* @example
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show">
* A simple example, animating the element's opacity:
*
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-simple">
<file name="index.html">
Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
<div>
Show:
<div class="check-element animate-show" ng-show="checked">
<span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
</div>
Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
<div class="check-element animate-show-hide" ng-show="checked">
I show up when your checkbox is checked.
</div>
<div>
Hide:
<div class="check-element animate-show" ng-hide="checked">
<span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
</div>
</div>
</file>
<file name="glyphicons.css">
@import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
</file>
<file name="animations.css">
.animate-show {
line-height: 20px;
opacity: 1;
padding: 10px;
border: 1px solid black;
background: white;
.animate-show-hide.ng-hide {
opacity: 0;
}
.animate-show.ng-hide-add, .animate-show.ng-hide-remove {
.animate-show-hide.ng-hide-add,
.animate-show-hide.ng-hide-remove {
transition: all linear 0.5s;
}
.animate-show.ng-hide {
line-height: 0;
opacity: 0;
padding: 0 10px;
}
.check-element {
padding: 10px;
border: 1px solid black;
background: white;
opacity: 1;
padding: 10px;
}
</file>
<file name="protractor.js" type="protractor">
var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
it('should check ngShow', function() {
var checkbox = element(by.model('checked'));
var checkElem = element(by.css('.check-element'));
it('should check ng-show / ng-hide', function() {
expect(thumbsUp.isDisplayed()).toBeFalsy();
expect(thumbsDown.isDisplayed()).toBeTruthy();
expect(checkElem.isDisplayed()).toBe(false);
checkbox.click();
expect(checkElem.isDisplayed()).toBe(true);
});
</file>
</example>
*
* <hr />
* @example
* A more complex example, featuring different show/hide animations:
*
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-show-complex">
<file name="index.html">
Show: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br />
<div class="check-element funky-show-hide" ng-show="checked">
I show up when your checkbox is checked.
</div>
</file>
<file name="animations.css">
body {
overflow: hidden;
perspective: 1000px;
}
element(by.model('checked')).click();
.funky-show-hide.ng-hide-add {
transform: rotateZ(0);
transform-origin: right;
transition: all 0.5s ease-in-out;
}
expect(thumbsUp.isDisplayed()).toBeTruthy();
expect(thumbsDown.isDisplayed()).toBeFalsy();
.funky-show-hide.ng-hide-add.ng-hide-add-active {
transform: rotateZ(-135deg);
}
.funky-show-hide.ng-hide-remove {
transform: rotateY(90deg);
transform-origin: left;
transition: all 0.5s ease;
}
.funky-show-hide.ng-hide-remove.ng-hide-remove-active {
transform: rotateY(0);
}
.check-element {
border: 1px solid black;
opacity: 1;
padding: 10px;
}
</file>
<file name="protractor.js" type="protractor">
it('should check ngShow', function() {
var checkbox = element(by.model('checked'));
var checkElem = element(by.css('.check-element'));
expect(checkElem.isDisplayed()).toBe(false);
checkbox.click();
expect(checkElem.isDisplayed()).toBe(true);
});
</file>
</example>
@@ -184,11 +210,13 @@ var ngShowDirective = ['$animate', function($animate) {
* @multiElement
*
* @description
* The `ngHide` directive shows or hides the given HTML element based on the expression
* provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
* the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
* in AngularJS and sets the display style to none (using an !important flag).
* For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
* The `ngHide` directive shows or hides the given HTML element based on the expression provided to
* the `ngHide` attribute.
*
* The element is shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
* The `.ng-hide` CSS class is predefined in AngularJS and sets the display style to none (using an
* `!important` flag). For CSP mode please add `angular-csp.css` to your HTML file (see
* {@link ng.directive:ngCsp ngCsp}).
*
* ```html
* <!-- when $scope.myValue is truthy (element is hidden) -->
@@ -198,30 +226,32 @@ var ngShowDirective = ['$animate', function($animate) {
* <div ng-hide="myValue"></div>
* ```
*
* When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
* attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
* from the element causing the element not to appear hidden.
* When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added
* to the class attribute on the element causing it to become hidden. When falsy, the `.ng-hide`
* CSS class is removed from the element causing the element not to appear hidden.
*
* ## Why is !important used?
* ## Why is `!important` used?
*
* You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
* can be easily overridden by heavier selectors. For example, something as simple
* as changing the display style on a HTML list item would make hidden elements appear visible.
* This also becomes a bigger issue when dealing with CSS frameworks.
* You may be wondering why `!important` is used for the `.ng-hide` CSS class. This is because the
* `.ng-hide` selector can be easily overridden by heavier selectors. For example, something as
* simple as changing the display style on a HTML list item would make hidden elements appear
* visible. This also becomes a bigger issue when dealing with CSS frameworks.
*
* By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
* specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
* styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
* By using `!important`, the show and hide behavior will work as expected despite any clash between
* CSS selector specificity (when `!important` isn't used with any conflicting styles). If a
* developer chooses to override the styling to change how to hide an element then it is just a
* matter of using `!important` in their own CSS code.
*
* ### Overriding `.ng-hide`
*
* By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
* the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
* class in CSS:
* By default, the `.ng-hide` class will style the element with `display: none !important`. If you
* wish to change the hide behavior with `ngShow`/`ngHide`, you can simply overwrite the styles for
* the `.ng-hide` CSS class. Note that the selector that needs to be used is actually
* `.ng-hide:not(.ng-hide-animate)` to cope with extra animation classes that can be added.
*
* ```css
* .ng-hide {
* /&#42; this is just another form of hiding an element &#42;/
* .ng-hide:not(.ng-hide-animate) {
* /&#42; These are just alternative ways of hiding an element &#42;/
* display: block!important;
* position: absolute;
* top: -9999px;
@@ -229,20 +259,20 @@ var ngShowDirective = ['$animate', function($animate) {
* }
* ```
*
* By default you don't need to override in CSS anything and the animations will work around the display style.
* By default you don't need to override in CSS anything and the animations will work around the
* display style.
*
* ## A note about animations with `ngHide`
*
* Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
* is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
* CSS class is added and removed for you instead of your own CSS class.
* Animations in `ngShow`/`ngHide` work with the show and hide events that are triggered when the
* directive expression is true and false. This system works like the animation system present with
* `ngClass` except that you must also include the `!important` flag to override the display
* property so that the elements are not actually hidden during the animation.
*
* ```css
* //
* //a working example can be found at the bottom of this page
* //
* /&#42; A working example can be found at the bottom of this page. &#42;/
* .my-element.ng-hide-add, .my-element.ng-hide-remove {
* transition: 0.5s linear all;
* transition: all 0.5s linear;
* }
*
* .my-element.ng-hide-add { ... }
@@ -251,74 +281,109 @@ var ngShowDirective = ['$animate', function($animate) {
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
* ```
*
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display property
* to block during animation states - ngAnimate will automatically handle the style toggling for you.
*
* @animations
* | Animation | Occurs |
* |----------------------------------|-------------------------------------|
* | {@link $animate#addClass addClass} `.ng-hide` | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden |
* | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible |
* | Animation | Occurs |
* |-----------------------------------------------------|------------------------------------------------------------------------------------------------------------|
* | {@link $animate#addClass addClass} `.ng-hide` | After the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden. |
* | {@link $animate#removeClass removeClass} `.ng-hide` | After the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible. |
*
*
* @element ANY
* @param {expression} ngHide If the {@link guide/expression expression} is truthy then
* the element is shown or hidden respectively.
* @param {expression} ngHide If the {@link guide/expression expression} is truthy/falsy then the
* element is hidden/shown respectively.
*
* @example
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide">
* A simple example, animating the element's opacity:
*
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-simple">
<file name="index.html">
Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
<div>
Show:
<div class="check-element animate-hide" ng-show="checked">
<span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
</div>
Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
<div class="check-element animate-show-hide" ng-hide="checked">
I hide when your checkbox is checked.
</div>
<div>
Hide:
<div class="check-element animate-hide" ng-hide="checked">
<span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
</div>
</div>
</file>
<file name="glyphicons.css">
@import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
</file>
<file name="animations.css">
.animate-hide {
transition: all linear 0.5s;
line-height: 20px;
opacity: 1;
padding: 10px;
border: 1px solid black;
background: white;
.animate-show-hide.ng-hide {
opacity: 0;
}
.animate-hide.ng-hide {
line-height: 0;
opacity: 0;
padding: 0 10px;
.animate-show-hide.ng-hide-add,
.animate-show-hide.ng-hide-remove {
transition: all linear 0.5s;
}
.check-element {
padding: 10px;
border: 1px solid black;
background: white;
opacity: 1;
padding: 10px;
}
</file>
<file name="protractor.js" type="protractor">
var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
it('should check ngHide', function() {
var checkbox = element(by.model('checked'));
var checkElem = element(by.css('.check-element'));
it('should check ng-show / ng-hide', function() {
expect(thumbsUp.isDisplayed()).toBeFalsy();
expect(thumbsDown.isDisplayed()).toBeTruthy();
expect(checkElem.isDisplayed()).toBe(true);
checkbox.click();
expect(checkElem.isDisplayed()).toBe(false);
});
</file>
</example>
*
* <hr />
* @example
* A more complex example, featuring different show/hide animations:
*
<example module="ngAnimate" deps="angular-animate.js" animations="true" name="ng-hide-complex">
<file name="index.html">
Hide: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br />
<div class="check-element funky-show-hide" ng-hide="checked">
I hide when your checkbox is checked.
</div>
</file>
<file name="animations.css">
body {
overflow: hidden;
perspective: 1000px;
}
element(by.model('checked')).click();
.funky-show-hide.ng-hide-add {
transform: rotateZ(0);
transform-origin: right;
transition: all 0.5s ease-in-out;
}
expect(thumbsUp.isDisplayed()).toBeTruthy();
expect(thumbsDown.isDisplayed()).toBeFalsy();
.funky-show-hide.ng-hide-add.ng-hide-add-active {
transform: rotateZ(-135deg);
}
.funky-show-hide.ng-hide-remove {
transform: rotateY(90deg);
transform-origin: left;
transition: all 0.5s ease;
}
.funky-show-hide.ng-hide-remove.ng-hide-remove-active {
transform: rotateY(0);
}
.check-element {
border: 1px solid black;
opacity: 1;
padding: 10px;
}
</file>
<file name="protractor.js" type="protractor">
it('should check ngHide', function() {
var checkbox = element(by.model('checked'));
var checkElem = element(by.css('.check-element'));
expect(checkElem.isDisplayed()).toBe(true);
checkbox.click();
expect(checkElem.isDisplayed()).toBe(false);
});
</file>
</example>
+38 -17
View File
@@ -4,6 +4,18 @@
var noopNgModelController = { $setViewValue: noop, $render: noop };
function setOptionSelectedStatus(optionEl, value) {
optionEl.prop('selected', value); // needed for IE
/**
* When unselecting an option, setting the property to null / false should be enough
* However, screenreaders might react to the selected attribute instead, see
* https://github.com/angular/angular.js/issues/14419
* Note: "selected" is a boolean attr and will be removed when the "value" arg in attr() is false
* or null
*/
optionEl.attr('selected', value);
}
/**
* @ngdoc type
* @name select.SelectController
@@ -16,7 +28,7 @@ var SelectController =
['$element', '$scope', /** @this */ function($element, $scope) {
var self = this,
optionsMap = new HashMap();
optionsMap = new NgMap();
self.selectValueMap = {}; // Keys are the hashed values, values the original values
@@ -44,14 +56,14 @@ var SelectController =
var unknownVal = self.generateUnknownOptionValue(val);
self.unknownOption.val(unknownVal);
$element.prepend(self.unknownOption);
setOptionAsSelected(self.unknownOption);
setOptionSelectedStatus(self.unknownOption, true);
$element.val(unknownVal);
};
self.updateUnknownOption = function(val) {
var unknownVal = self.generateUnknownOptionValue(val);
self.unknownOption.val(unknownVal);
setOptionAsSelected(self.unknownOption);
setOptionSelectedStatus(self.unknownOption, true);
$element.val(unknownVal);
};
@@ -66,7 +78,7 @@ var SelectController =
self.selectEmptyOption = function() {
if (self.emptyOption) {
$element.val('');
setOptionAsSelected(self.emptyOption);
setOptionSelectedStatus(self.emptyOption, true);
}
};
@@ -102,7 +114,7 @@ var SelectController =
// Make sure to remove the selected attribute from the previously selected option
// Otherwise, screen readers might get confused
var currentlySelectedOption = $element[0].options[$element[0].selectedIndex];
if (currentlySelectedOption) currentlySelectedOption.removeAttribute('selected');
if (currentlySelectedOption) setOptionSelectedStatus(jqLite(currentlySelectedOption), false);
if (self.hasOption(value)) {
self.removeUnknownOption();
@@ -112,7 +124,7 @@ var SelectController =
// Set selected attribute and property on selected option for screen readers
var selectedOption = $element[0].options[$element[0].selectedIndex];
setOptionAsSelected(jqLite(selectedOption));
setOptionSelectedStatus(jqLite(selectedOption), true);
} else {
if (value == null && self.emptyOption) {
self.removeUnknownOption();
@@ -137,7 +149,7 @@ var SelectController =
self.emptyOption = element;
}
var count = optionsMap.get(value) || 0;
optionsMap.put(value, count + 1);
optionsMap.set(value, count + 1);
// Only render at the end of a digest. This improves render performance when many options
// are added during a digest and ensures all relevant options are correctly marked as selected
scheduleRender();
@@ -148,13 +160,13 @@ var SelectController =
var count = optionsMap.get(value);
if (count) {
if (count === 1) {
optionsMap.remove(value);
optionsMap.delete(value);
if (value === '') {
self.hasEmptyOption = false;
self.emptyOption = undefined;
}
} else {
optionsMap.put(value, count - 1);
optionsMap.set(value, count - 1);
}
}
};
@@ -281,7 +293,7 @@ var SelectController =
var removeValue = optionAttrs.value;
self.removeOption(removeValue);
self.ngModelCtrl.$render();
scheduleRender();
if (self.multiple && currentValue && currentValue.indexOf(removeValue) !== -1 ||
currentValue === removeValue
@@ -292,11 +304,6 @@ var SelectController =
}
});
};
function setOptionAsSelected(optionEl) {
optionEl.prop('selected', true); // needed for IE
optionEl.attr('selected', true);
}
}];
/**
@@ -366,6 +373,8 @@ var SelectController =
* interaction with the select element.
* @param {string=} ngOptions sets the options that the select is populated with and defines what is
* set on the model on selection. See {@link ngOptions `ngOptions`}.
* @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
* {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
*
* @example
* ### Simple `select` elements with static options
@@ -606,9 +615,21 @@ var selectDirective = function() {
// Write value now needs to set the selected property of each matching option
selectCtrl.writeValue = function writeMultipleValue(value) {
var items = new HashMap(value);
forEach(element.find('option'), function(option) {
option.selected = isDefined(items.get(option.value)) || isDefined(items.get(selectCtrl.selectValueMap[option.value]));
var shouldBeSelected = !!value && (includes(value, option.value) ||
includes(value, selectCtrl.selectValueMap[option.value]));
var currentlySelected = option.selected;
// IE and Edge, adding options to the selection via shift+click/UP/DOWN,
// will de-select already selected options if "selected" on those options was set
// more than once (i.e. when the options were already selected)
// So we only modify the selected property if neccessary.
// Note: this behavior cannot be replicated via unit tests because it only shows in the
// actual user interface.
if (shouldBeSelected !== currentlySelected) {
setOptionSelectedStatus(jqLite(option), shouldBeSelected);
}
});
};
+7 -1
View File
@@ -9,6 +9,9 @@
* Selects a subset of items from `array` and returns it as a new array.
*
* @param {Array} array The source array.
* <div class="alert alert-info">
* **Note**: If the array contains objects that reference themselves, filtering is not possible.
* </div>
* @param {string|Object|function()} expression The predicate to be used for selecting items from
* `array`.
*
@@ -226,7 +229,10 @@ function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstA
var key;
if (matchAgainstAnyProp) {
for (key in actual) {
if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
// Under certain, rare, circumstances, key may not be a string and `charAt` will be undefined
// See: https://github.com/angular/angular.js/issues/15644
if (key.charAt && (key.charAt(0) !== '$') &&
deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
return true;
}
}
+2 -1
View File
@@ -1255,7 +1255,8 @@ function $HttpProvider() {
if ((config.cache || defaults.cache) && config.cache !== false &&
(config.method === 'GET' || config.method === 'JSONP')) {
cache = isObject(config.cache) ? config.cache
: isObject(defaults.cache) ? defaults.cache
: isObject(/** @type {?} */ (defaults).cache)
? /** @type {?} */ (defaults).cache
: defaultCache;
}
+2 -1
View File
@@ -33,7 +33,8 @@ function $IntervalProvider() {
* appropriate moment. See the example below for more details on how and when to do this.
* </div>
*
* @param {function()} fn A function that should be called repeatedly.
* @param {function()} fn A function that should be called repeatedly. If no additional arguments
* are passed (see below), the function is called with the current iteration count.
* @param {number} delay Number of milliseconds between each function call.
* @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
* indefinitely.
+3 -3
View File
@@ -10,8 +10,8 @@
* how they vary compared to the requested url.
*/
var $jsonpCallbacksProvider = /** @this */ function() {
this.$get = ['$window', function($window) {
var callbacks = $window.angular.callbacks;
this.$get = function() {
var callbacks = angular.callbacks;
var callbackMap = {};
function createCallback(callbackId) {
@@ -78,5 +78,5 @@ var $jsonpCallbacksProvider = /** @this */ function() {
delete callbackMap[callbackPath];
}
};
}];
};
};
+37 -26
View File
@@ -137,6 +137,8 @@ function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
this.$$urlUpdatedByLocation = true;
};
this.$$parseLinkUrl = function(url, relHref) {
@@ -214,7 +216,7 @@ function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
withoutHashUrl = '';
if (isUndefined(withoutBaseUrl)) {
appBase = url;
this.replace();
/** @type {?} */ (this).replace();
}
}
}
@@ -270,6 +272,8 @@ function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
this.$$urlUpdatedByLocation = true;
};
this.$$parseLinkUrl = function(url, relHref) {
@@ -327,6 +331,8 @@ function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
// include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
this.$$absUrl = appBase + hashPrefix + this.$$url;
this.$$urlUpdatedByLocation = true;
};
}
@@ -656,6 +662,7 @@ forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], fun
// but we're changing the $$state reference to $browser.state() during the $digest
// so the modification window is narrow.
this.$$state = isUndefined(state) ? null : state;
this.$$urlUpdatedByLocation = true;
return this;
};
@@ -968,36 +975,40 @@ function $LocationProvider() {
// update browser
$rootScope.$watch(function $locationWatch() {
var oldUrl = trimEmptyHash($browser.url());
var newUrl = trimEmptyHash($location.absUrl());
var oldState = $browser.state();
var currentReplace = $location.$$replace;
var urlOrStateChanged = oldUrl !== newUrl ||
($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
if (initializing || $location.$$urlUpdatedByLocation) {
$location.$$urlUpdatedByLocation = false;
if (initializing || urlOrStateChanged) {
initializing = false;
var oldUrl = trimEmptyHash($browser.url());
var newUrl = trimEmptyHash($location.absUrl());
var oldState = $browser.state();
var currentReplace = $location.$$replace;
var urlOrStateChanged = oldUrl !== newUrl ||
($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
$rootScope.$evalAsync(function() {
var newUrl = $location.absUrl();
var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
$location.$$state, oldState).defaultPrevented;
if (initializing || urlOrStateChanged) {
initializing = false;
// if the location was changed by a `$locationChangeStart` handler then stop
// processing this location change
if ($location.absUrl() !== newUrl) return;
$rootScope.$evalAsync(function() {
var newUrl = $location.absUrl();
var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
$location.$$state, oldState).defaultPrevented;
if (defaultPrevented) {
$location.$$parse(oldUrl);
$location.$$state = oldState;
} else {
if (urlOrStateChanged) {
setBrowserUrlWithFallback(newUrl, currentReplace,
oldState === $location.$$state ? null : $location.$$state);
// if the location was changed by a `$locationChangeStart` handler then stop
// processing this location change
if ($location.absUrl() !== newUrl) return;
if (defaultPrevented) {
$location.$$parse(oldUrl);
$location.$$state = oldState;
} else {
if (urlOrStateChanged) {
setBrowserUrlWithFallback(newUrl, currentReplace,
oldState === $location.$$state ? null : $location.$$state);
}
afterLocationChange(oldUrl, oldState);
}
afterLocationChange(oldUrl, oldState);
}
});
});
}
}
$location.$$replace = false;
+11 -2
View File
@@ -60,13 +60,22 @@ function $LogProvider() {
this.debugEnabled = function(flag) {
if (isDefined(flag)) {
debug = flag;
return this;
return this;
} else {
return debug;
}
};
this.$get = ['$window', function($window) {
// Support: IE 9-11, Edge 12-14+
// IE/Edge display errors in such a way that it requires the user to click in 4 places
// to see the stack trace. There is no way to feature-detect it so there's a chance
// of the user agent sniffing to go wrong but since it's only about logging, this shouldn't
// break apps. Other browsers display errors in a sensible way and some of them map stack
// traces along source maps if available so it makes sense to let browsers display it
// as they want.
var formatStackTrace = msie || /\bEdge\//.test($window.navigator && $window.navigator.userAgent);
return {
/**
* @ngdoc method
@@ -124,7 +133,7 @@ function $LogProvider() {
function formatError(arg) {
if (arg instanceof Error) {
if (arg.stack) {
if (arg.stack && formatStackTrace) {
arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
? 'Error: ' + arg.message + '\n' + arg.stack
: arg.stack;
+11 -4
View File
@@ -717,6 +717,13 @@ function findConstantAndWatchExpressions(ast, $filter) {
if (!property.value.constant) {
argsToWatch.push.apply(argsToWatch, property.value.toWatch);
}
if (property.computed) {
findConstantAndWatchExpressions(property.key, $filter);
if (!property.key.constant) {
argsToWatch.push.apply(argsToWatch, property.key.toWatch);
}
}
});
ast.constant = allConstants;
ast.toWatch = argsToWatch;
@@ -1782,13 +1789,13 @@ function $ParseProvider() {
}
}
function expressionInputDirtyCheck(newValue, oldValueOfValue) {
function expressionInputDirtyCheck(newValue, oldValueOfValue, compareObjectIdentity) {
if (newValue == null || oldValueOfValue == null) { // null/undefined
return newValue === oldValueOfValue;
}
if (typeof newValue === 'object') {
if (typeof newValue === 'object' && !compareObjectIdentity) {
// attempt to convert the value to a primitive type
// TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
@@ -1817,7 +1824,7 @@ function $ParseProvider() {
inputExpressions = inputExpressions[0];
return scope.$watch(function expressionInputWatch(scope) {
var newInputValue = inputExpressions(scope);
if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf, parsedExpression.literal)) {
lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
oldInputValueOf = newInputValue && getValueOf(newInputValue);
}
@@ -1837,7 +1844,7 @@ function $ParseProvider() {
for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
var newInputValue = inputExpressions[i](scope);
if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i], parsedExpression.literal))) {
oldInputValues[i] = newInputValue;
oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
}
+6 -1
View File
@@ -239,6 +239,7 @@ function $QProvider() {
*
* @description
* Retrieves or overrides whether to generate an error when a rejected promise is not handled.
* This feature is enabled by default.
*
* @param {boolean=} value Whether to generate an error when a rejected promise is not handled.
* @returns {boolean|ng.$qProvider} Current value when called without a new value or self for
@@ -380,7 +381,11 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
if (!toCheck.pur) {
toCheck.pur = true;
var errorMessage = 'Possibly unhandled rejection: ' + toDebugString(toCheck.value);
exceptionHandler(errorMessage);
if (toCheck.value instanceof Error) {
exceptionHandler(toCheck.value, errorMessage);
} else {
exceptionHandler(errorMessage);
}
}
}
}
+18 -8
View File
@@ -416,15 +416,21 @@ function $RootScopeProvider() {
if (!array) {
array = scope.$$watchers = [];
array.$$digestWatchIndex = -1;
}
// we use unshift since we use a while loop in $digest for speed.
// the while loop reads in reverse order.
array.unshift(watcher);
array.$$digestWatchIndex++;
incrementWatchersCount(this, 1);
return function deregisterWatch() {
if (arrayRemove(array, watcher) >= 0) {
var index = arrayRemove(array, watcher);
if (index >= 0) {
incrementWatchersCount(scope, -1);
if (index < array.$$digestWatchIndex) {
array.$$digestWatchIndex--;
}
}
lastDirtyWatch = null;
};
@@ -757,7 +763,6 @@ function $RootScopeProvider() {
$digest: function() {
var watch, value, last, fn, get,
watchers,
length,
dirty, ttl = TTL,
next, current, target = this,
watchLog = [],
@@ -781,12 +786,13 @@ function $RootScopeProvider() {
current = target;
// It's safe for asyncQueuePosition to be a local variable here because this loop can't
// be reentered recursively. Calling $digest from a function passed to $applyAsync would
// be reentered recursively. Calling $digest from a function passed to $evalAsync would
// lead to a '$digest already in progress' error.
for (var asyncQueuePosition = 0; asyncQueuePosition < asyncQueue.length; asyncQueuePosition++) {
try {
asyncTask = asyncQueue[asyncQueuePosition];
asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
fn = asyncTask.fn;
fn(asyncTask.scope, asyncTask.locals);
} catch (e) {
$exceptionHandler(e);
}
@@ -798,10 +804,10 @@ function $RootScopeProvider() {
do { // "traverse the scopes" loop
if ((watchers = current.$$watchers)) {
// process our watches
length = watchers.length;
while (length--) {
watchers.$$digestWatchIndex = watchers.length;
while (watchers.$$digestWatchIndex--) {
try {
watch = watchers[length];
watch = watchers[watchers.$$digestWatchIndex];
// Most common watches are on primitives, in which case we can short
// circuit it with === operator, only when === fails do we use .equals
if (watch) {
@@ -871,6 +877,10 @@ function $RootScopeProvider() {
}
}
postDigestQueue.length = postDigestQueuePosition = 0;
// Check for changes to browser url that happened during the $digest
// (for which no event is fired; e.g. via `history.pushState()`)
$browser.$$checkUrlChange();
},
@@ -1016,7 +1026,7 @@ function $RootScopeProvider() {
});
}
asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
asyncQueue.push({scope: this, fn: $parse(expr), locals: locals});
},
$$postDigest: function(fn) {
+3
View File
@@ -25,7 +25,10 @@ function $SnifferProvider() {
// (see https://developer.chrome.com/apps/api_index). If sandboxed, they can be detected by
// the presence of an extension runtime ID and the absence of other Chrome runtime APIs
// (see https://developer.chrome.com/apps/manifest/sandbox).
// (NW.js apps have access to Chrome APIs, but do support `history`.)
isNw = $window.nw && $window.nw.process,
isChromePackagedApp =
!isNw &&
$window.chrome &&
($window.chrome.app && $window.chrome.app.runtime ||
!$window.chrome.app && $window.chrome.runtime && $window.chrome.runtime.id),
+1 -1
View File
@@ -19,7 +19,7 @@ var originUrl = urlResolve(window.location.href);
* URL will be resolved into an absolute URL in the context of the application document.
* Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
* properties are all populated to reflect the normalized URL. This approach has wide
* compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
* compatibility - Safari 1+, Mozilla 1+ etc. See
* http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
*
* Implementation Notes for IE
+79 -83
View File
@@ -36,9 +36,9 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
}
}
function isAllowed(ruleType, element, currentAnimation, previousAnimation) {
function isAllowed(ruleType, currentAnimation, previousAnimation) {
return rules[ruleType].some(function(fn) {
return fn(element, currentAnimation, previousAnimation);
return fn(currentAnimation, previousAnimation);
});
}
@@ -48,40 +48,40 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
return and ? a && b : a || b;
}
rules.join.push(function(element, newAnimation, currentAnimation) {
rules.join.push(function(newAnimation, currentAnimation) {
// if the new animation is class-based then we can just tack that on
return !newAnimation.structural && hasAnimationClasses(newAnimation);
});
rules.skip.push(function(element, newAnimation, currentAnimation) {
rules.skip.push(function(newAnimation, currentAnimation) {
// there is no need to animate anything if no classes are being added and
// there is no structural animation that will be triggered
return !newAnimation.structural && !hasAnimationClasses(newAnimation);
});
rules.skip.push(function(element, newAnimation, currentAnimation) {
rules.skip.push(function(newAnimation, currentAnimation) {
// why should we trigger a new structural animation if the element will
// be removed from the DOM anyway?
return currentAnimation.event === 'leave' && newAnimation.structural;
});
rules.skip.push(function(element, newAnimation, currentAnimation) {
rules.skip.push(function(newAnimation, currentAnimation) {
// if there is an ongoing current animation then don't even bother running the class-based animation
return currentAnimation.structural && currentAnimation.state === RUNNING_STATE && !newAnimation.structural;
});
rules.cancel.push(function(element, newAnimation, currentAnimation) {
rules.cancel.push(function(newAnimation, currentAnimation) {
// there can never be two structural animations running at the same time
return currentAnimation.structural && newAnimation.structural;
});
rules.cancel.push(function(element, newAnimation, currentAnimation) {
rules.cancel.push(function(newAnimation, currentAnimation) {
// if the previous animation is already running, but the new animation will
// be triggered, but the new animation is structural
return currentAnimation.state === RUNNING_STATE && newAnimation.structural;
});
rules.cancel.push(function(element, newAnimation, currentAnimation) {
rules.cancel.push(function(newAnimation, currentAnimation) {
// cancel the animation if classes added / removed in both animation cancel each other out,
// but only if the current animation isn't structural
@@ -100,15 +100,15 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
return hasMatchingClasses(nA, cR) || hasMatchingClasses(nR, cA);
});
this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$HashMap',
this.$get = ['$$rAF', '$rootScope', '$rootElement', '$document', '$$Map',
'$$animation', '$$AnimateRunner', '$templateRequest', '$$jqLite', '$$forceReflow',
'$$isDocumentHidden',
function($$rAF, $rootScope, $rootElement, $document, $$HashMap,
function($$rAF, $rootScope, $rootElement, $document, $$Map,
$$animation, $$AnimateRunner, $templateRequest, $$jqLite, $$forceReflow,
$$isDocumentHidden) {
var activeAnimationsLookup = new $$HashMap();
var disabledElementsLookup = new $$HashMap();
var activeAnimationsLookup = new $$Map();
var disabledElementsLookup = new $$Map();
var animationsEnabled = null;
function postDigestTaskFactory() {
@@ -181,10 +181,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
return this === arg || !!(this.compareDocumentPosition(arg) & 16);
};
function findCallbacks(parent, element, event) {
var targetNode = getDomNode(element);
var targetParentNode = getDomNode(parent);
function findCallbacks(targetParentNode, targetNode, event) {
var matches = [];
var entries = callbackRegistry[event];
if (entries) {
@@ -209,11 +206,11 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
});
}
function cleanupEventListeners(phase, element) {
if (phase === 'close' && !element[0].parentNode) {
function cleanupEventListeners(phase, node) {
if (phase === 'close' && !node.parentNode) {
// If the element is not attached to a parentNode, it has been removed by
// the domOperation, and we can safely remove the event callbacks
$animate.off(element);
$animate.off(node);
}
}
@@ -294,7 +291,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
bool = !disabledElementsLookup.get(node);
} else {
// (element, bool) - Element setter
disabledElementsLookup.put(node, !bool);
disabledElementsLookup.set(node, !bool);
}
}
}
@@ -305,18 +302,15 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
return $animate;
function queueAnimation(element, event, initialOptions) {
function queueAnimation(originalElement, 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) {
node = getDomNode(element);
parent = element.parent();
}
var element = stripCommentsFromElement(originalElement);
var node = getDomNode(element);
var parentNode = node && node.parentNode;
options = prepareAnimationOptions(options);
@@ -381,7 +375,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
// there is no point in traversing the same collection of parent ancestors if a followup
// animation will be run on the same element that already did all that checking work
if (!skipAnimations && (!hasExistingAnimation || existingAnimation.state !== PRE_DIGEST_STATE)) {
skipAnimations = !areAnimationsAllowed(element, parent, event);
skipAnimations = !areAnimationsAllowed(node, parentNode, event);
}
if (skipAnimations) {
@@ -393,7 +387,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
}
if (isStructural) {
closeChildAnimations(element);
closeChildAnimations(node);
}
var newAnimation = {
@@ -408,7 +402,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
};
if (hasExistingAnimation) {
var skipAnimationFlag = isAllowed('skip', element, newAnimation, existingAnimation);
var skipAnimationFlag = isAllowed('skip', newAnimation, existingAnimation);
if (skipAnimationFlag) {
if (existingAnimation.state === RUNNING_STATE) {
close();
@@ -418,7 +412,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
return existingAnimation.runner;
}
}
var cancelAnimationFlag = isAllowed('cancel', element, newAnimation, existingAnimation);
var cancelAnimationFlag = isAllowed('cancel', newAnimation, existingAnimation);
if (cancelAnimationFlag) {
if (existingAnimation.state === RUNNING_STATE) {
// this will end the animation right away and it is safe
@@ -440,7 +434,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
// a joined animation means that this animation will take over the existing one
// so an example would involve a leave animation taking over an enter. Then when
// the postDigest kicks in the enter will be ignored.
var joinAnimationFlag = isAllowed('join', element, newAnimation, existingAnimation);
var joinAnimationFlag = isAllowed('join', newAnimation, existingAnimation);
if (joinAnimationFlag) {
if (existingAnimation.state === RUNNING_STATE) {
normalizeAnimationDetails(element, newAnimation);
@@ -474,7 +468,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
if (!isValidAnimation) {
close();
clearElementAnimationState(element);
clearElementAnimationState(node);
return runner;
}
@@ -482,9 +476,18 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
var counter = (existingAnimation.counter || 0) + 1;
newAnimation.counter = counter;
markElementAnimationState(element, PRE_DIGEST_STATE, newAnimation);
markElementAnimationState(node, PRE_DIGEST_STATE, newAnimation);
$rootScope.$$postDigest(function() {
// It is possible that the DOM nodes inside `originalElement` have been replaced. This can
// happen if the animated element is a transcluded clone and also has a `templateUrl`
// directive on it. Therefore, we must recreate `element` in order to interact with the
// actual DOM nodes.
// Note: We still need to use the old `node` for certain things, such as looking up in
// HashMaps where it was used as the key.
element = stripCommentsFromElement(originalElement);
var animationDetails = activeAnimationsLookup.get(node);
var animationCancelled = !animationDetails;
animationDetails = animationDetails || {};
@@ -523,7 +526,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
// isn't allowed to animate from here then we need to clear the state of the element
// so that any future animations won't read the expired animation data.
if (!isValidAnimation) {
clearElementAnimationState(element);
clearElementAnimationState(node);
}
return;
@@ -535,7 +538,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
? 'setClass'
: animationDetails.event;
markElementAnimationState(element, RUNNING_STATE);
markElementAnimationState(node, RUNNING_STATE);
var realRunner = $$animation(element, event, animationDetails.options);
// this will update the runner's flow-control events based on
@@ -547,7 +550,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
close(!status);
var animationDetails = activeAnimationsLookup.get(node);
if (animationDetails && animationDetails.counter === counter) {
clearElementAnimationState(getDomNode(element));
clearElementAnimationState(node);
}
notifyProgress(runner, event, 'close', {});
});
@@ -557,7 +560,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
function notifyProgress(runner, event, phase, data) {
runInNextPostDigestOrNow(function() {
var callbacks = findCallbacks(parent, element, event);
var callbacks = findCallbacks(parentNode, node, event);
if (callbacks.length) {
// do not optimize this call here to RAF because
// we don't know how heavy the callback code here will
@@ -567,10 +570,10 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
forEach(callbacks, function(callback) {
callback(element, phase, data);
});
cleanupEventListeners(phase, element);
cleanupEventListeners(phase, node);
});
} else {
cleanupEventListeners(phase, element);
cleanupEventListeners(phase, node);
}
});
runner.progress(event, phase, data);
@@ -585,8 +588,7 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
}
}
function closeChildAnimations(element) {
var node = getDomNode(element);
function closeChildAnimations(node) {
var children = node.querySelectorAll('[' + NG_ANIMATE_ATTR_NAME + ']');
forEach(children, function(child) {
var state = parseInt(child.getAttribute(NG_ANIMATE_ATTR_NAME), 10);
@@ -597,21 +599,16 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
animationDetails.runner.end();
/* falls through */
case PRE_DIGEST_STATE:
activeAnimationsLookup.remove(child);
activeAnimationsLookup.delete(child);
break;
}
}
});
}
function clearElementAnimationState(element) {
var node = getDomNode(element);
function clearElementAnimationState(node) {
node.removeAttribute(NG_ANIMATE_ATTR_NAME);
activeAnimationsLookup.remove(node);
}
function isMatchingElement(nodeOrElmA, nodeOrElmB) {
return getDomNode(nodeOrElmA) === getDomNode(nodeOrElmB);
activeAnimationsLookup.delete(node);
}
/**
@@ -621,54 +618,54 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
* c) the element is not a child of the body
* d) the element is not a child of the $rootElement
*/
function areAnimationsAllowed(element, parentElement, event) {
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;
var elementDisabled = disabledElementsLookup.get(getDomNode(element));
function areAnimationsAllowed(node, parentNode, event) {
var bodyNode = $document[0].body;
var rootNode = getDomNode($rootElement);
var parentHost = jqLite.data(element[0], NG_ANIMATE_PIN_DATA);
var bodyNodeDetected = (node === bodyNode) || node.nodeName === 'HTML';
var rootNodeDetected = (node === rootNode);
var parentAnimationDetected = false;
var elementDisabled = disabledElementsLookup.get(node);
var animateChildren;
var parentHost = jqLite.data(node, NG_ANIMATE_PIN_DATA);
if (parentHost) {
parentElement = parentHost;
parentNode = getDomNode(parentHost);
}
parentElement = getDomNode(parentElement);
while (parentElement) {
if (!rootElementDetected) {
while (parentNode) {
if (!rootNodeDetected) {
// angular doesn't want to attempt to animate elements outside of the application
// therefore we need to ensure that the rootElement is an ancestor of the current element
rootElementDetected = isMatchingElement(parentElement, $rootElement);
rootNodeDetected = (parentNode === rootNode);
}
if (parentElement.nodeType !== ELEMENT_NODE) {
if (parentNode.nodeType !== ELEMENT_NODE) {
// no point in inspecting the #document element
break;
}
var details = activeAnimationsLookup.get(parentElement) || {};
var details = activeAnimationsLookup.get(parentNode) || {};
// either an enter, leave or move animation will commence
// therefore we can't allow any animations to take place
// but if a parent animation is class-based then that's ok
if (!parentAnimationDetected) {
var parentElementDisabled = disabledElementsLookup.get(parentElement);
var parentNodeDisabled = disabledElementsLookup.get(parentNode);
if (parentElementDisabled === true && elementDisabled !== false) {
if (parentNodeDisabled === true && elementDisabled !== false) {
// disable animations if the user hasn't explicitly enabled animations on the
// current element
elementDisabled = true;
// element is disabled via parent element, no need to check anything else
break;
} else if (parentElementDisabled === false) {
} else if (parentNodeDisabled === false) {
elementDisabled = false;
}
parentAnimationDetected = details.structural;
}
if (isUndefined(animateChildren) || animateChildren === true) {
var value = jqLite.data(parentElement, NG_ANIMATE_CHILDREN_DATA);
var value = jqLite.data(parentNode, NG_ANIMATE_CHILDREN_DATA);
if (isDefined(value)) {
animateChildren = value;
}
@@ -677,47 +674,46 @@ var $$AnimateQueueProvider = ['$animateProvider', /** @this */ function($animate
// there is no need to continue traversing at this point
if (parentAnimationDetected && animateChildren === false) break;
if (!bodyElementDetected) {
if (!bodyNodeDetected) {
// we also need to ensure that the element is or will be a part of the body element
// otherwise it is pointless to even issue an animation to be rendered
bodyElementDetected = isMatchingElement(parentElement, bodyElement);
bodyNodeDetected = (parentNode === bodyNode);
}
if (bodyElementDetected && rootElementDetected) {
if (bodyNodeDetected && rootNodeDetected) {
// If both body and root have been found, any other checks are pointless,
// as no animation data should live outside the application
break;
}
if (!rootElementDetected) {
// If no rootElement is detected, check if the parentElement is pinned to another element
parentHost = jqLite.data(parentElement, NG_ANIMATE_PIN_DATA);
if (!rootNodeDetected) {
// If `rootNode` is not detected, check if `parentNode` is pinned to another element
parentHost = jqLite.data(parentNode, NG_ANIMATE_PIN_DATA);
if (parentHost) {
// The pin target element becomes the next parent element
parentElement = getDomNode(parentHost);
parentNode = getDomNode(parentHost);
continue;
}
}
parentElement = parentElement.parentNode;
parentNode = parentNode.parentNode;
}
var allowAnimation = (!parentAnimationDetected || animateChildren) && elementDisabled !== true;
return allowAnimation && rootElementDetected && bodyElementDetected;
return allowAnimation && rootNodeDetected && bodyNodeDetected;
}
function markElementAnimationState(element, state, details) {
function markElementAnimationState(node, state, details) {
details = details || {};
details.state = state;
var node = getDomNode(element);
node.setAttribute(NG_ANIMATE_ATTR_NAME, state);
var oldValue = activeAnimationsLookup.get(node);
var newValue = oldValue
? extend(oldValue, details)
: details;
activeAnimationsLookup.put(node, newValue);
activeAnimationsLookup.set(node, newValue);
}
}];
}];
+6 -6
View File
@@ -21,21 +21,21 @@ var $$AnimationProvider = ['$animateProvider', /** @this */ function($animatePro
return element.data(RUNNER_STORAGE_KEY);
}
this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$HashMap', '$$rAFScheduler',
function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$HashMap, $$rAFScheduler) {
this.$get = ['$$jqLite', '$rootScope', '$injector', '$$AnimateRunner', '$$Map', '$$rAFScheduler',
function($$jqLite, $rootScope, $injector, $$AnimateRunner, $$Map, $$rAFScheduler) {
var animationQueue = [];
var applyAnimationClasses = applyAnimationClassesFactory($$jqLite);
function sortAnimations(animations) {
var tree = { children: [] };
var i, lookup = new $$HashMap();
var i, lookup = new $$Map();
// this is done first beforehand so that the hashmap
// this is done first beforehand so that the map
// is filled with a list of the elements that will be animated
for (i = 0; i < animations.length; i++) {
var animation = animations[i];
lookup.put(animation.domNode, animations[i] = {
lookup.set(animation.domNode, animations[i] = {
domNode: animation.domNode,
fn: animation.fn,
children: []
@@ -54,7 +54,7 @@ var $$AnimationProvider = ['$animateProvider', /** @this */ function($animatePro
var elementNode = entry.domNode;
var parentNode = elementNode.parentNode;
lookup.put(elementNode, entry);
lookup.set(elementNode, entry);
var parentEntry;
while (parentNode) {
+5
View File
@@ -177,6 +177,10 @@
* /&#42; As of 1.4.4, this must always be set: it signals ngAnimate
* to not accidentally inherit a delay property from another CSS class &#42;/
* transition-duration: 0s;
*
* /&#42; if you are using animations instead of transitions you should configure as follows:
* animation-delay: 0.1s;
* animation-duration: 0s; &#42;/
* }
* .my-animation.ng-enter.ng-enter-active {
* /&#42; standard transition styles &#42;/
@@ -756,6 +760,7 @@ angular.module('ngAnimate', [], function initAngularHelpers() {
isFunction = angular.isFunction;
isElement = angular.isElement;
})
.info({ angularVersion: '"NG_VERSION_FULL"' })
.directive('ngAnimateSwap', ngAnimateSwapDirective)
.directive('ngAnimateChildren', $$AnimateChildrenDirective)
+2 -1
View File
@@ -54,6 +54,7 @@
* {@link guide/accessibility Developer Guide}.
*/
var ngAriaModule = angular.module('ngAria', ['ng']).
info({ angularVersion: '"NG_VERSION_FULL"' }).
provider('$aria', $AriaProvider);
/**
@@ -355,7 +356,7 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
return {
restrict: 'A',
compile: function(elem, attr) {
var fn = $parse(attr.ngClick, /* interceptorFn */ null, /* expensiveChecks */ true);
var fn = $parse(attr.ngClick);
return function(scope, elem, attr) {
if (!isNodeOneOf(elem, nodeBlackList)) {
+1
View File
@@ -17,6 +17,7 @@
angular.module('ngCookies', ['ng']).
info({ angularVersion: '"NG_VERSION_FULL"' }).
/**
* @ngdoc provider
* @name $cookiesProvider
@@ -216,6 +216,7 @@ var toJson;
var $$stringify;
var module = window['angular']['module']('ngMessageFormat', ['ng']);
module['info']({ 'angularVersion': '"NG_VERSION_FULL"' });
module['factory']('$$messageFormat', $$MessageFormatFactory);
module['config'](['$provide', function($provide) {
$interpolateMinErr = window['angular']['$interpolateMinErr'];
+3 -4
View File
@@ -267,6 +267,7 @@ angular.module('ngMessages', [], function initAngularHelpers() {
isString = angular.isString;
jqLite = angular.element;
})
.info({ angularVersion: '"NG_VERSION_FULL"' })
/**
* @ngdoc directive
@@ -629,10 +630,8 @@ angular.module('ngMessages', [], function initAngularHelpers() {
* @scope
*
* @description
* `ngMessageExp` is a directive with the purpose to show and hide a particular message.
* For `ngMessageExp` to operate, a parent `ngMessages` directive on a parent DOM element
* must be situated since it determines which messages are visible based on the state
* of the provided key/value map that `ngMessages` listens on.
* `ngMessageExp` is the same as {@link directive:ngMessage `ngMessage`}, but instead of a static
* value, it accepts an expression to be evaluated for the message key.
*
* @usage
* ```html
+38 -22
View File
@@ -37,10 +37,30 @@ angular.mock.$Browser = function() {
self.$$lastUrl = self.$$url; // used by url polling fn
self.pollFns = [];
// TODO(vojta): remove this temporary api
self.$$completeOutstandingRequest = angular.noop;
self.$$incOutstandingRequestCount = angular.noop;
// Testability API
var outstandingRequestCount = 0;
var outstandingRequestCallbacks = [];
self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
self.$$completeOutstandingRequest = function(fn) {
try {
fn();
} finally {
outstandingRequestCount--;
if (!outstandingRequestCount) {
while (outstandingRequestCallbacks.length) {
outstandingRequestCallbacks.pop()();
}
}
}
};
self.notifyWhenNoOutstandingRequests = function(callback) {
if (outstandingRequestCount) {
outstandingRequestCallbacks.push(callback);
} else {
callback();
}
};
// register url polling fn
@@ -65,6 +85,8 @@ angular.mock.$Browser = function() {
self.deferredNextId = 0;
self.defer = function(fn, delay) {
// Note that we do not use `$$incOutstandingRequestCount` or `$$completeOutstandingRequest`
// in this mock implementation.
delay = delay || 0;
self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
self.deferredFns.sort(function(a, b) { return a.time - b.time;});
@@ -166,10 +188,6 @@ angular.mock.$Browser.prototype = {
state: function() {
return this.$$state;
},
notifyWhenNoOutstandingRequests: function(fn) {
fn();
}
};
@@ -772,6 +790,7 @@ angular.mock.TzDate.prototype = Date.prototype;
* You need to require the `ngAnimateMock` module in your test suite for instance `beforeEach(module('ngAnimateMock'))`
*/
angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
.info({ angularVersion: '"NG_VERSION_FULL"' })
.config(['$provide', function($provide) {
@@ -1295,9 +1314,8 @@ angular.mock.dump = function(object) {
});
```
*/
angular.mock.$HttpBackendProvider = function() {
this.$get = ['$rootScope', '$timeout', createHttpBackendMock];
};
angular.mock.$httpBackendDecorator =
['$rootScope', '$timeout', '$delegate', createHttpBackendMock];
/**
* General factory function for $httpBackend mock.
@@ -1318,7 +1336,10 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
expectations = [],
responses = [],
responsesPush = angular.bind(responses, responses.push),
copy = angular.copy;
copy = angular.copy,
// We cache the original backend so that if both ngMock and ngMockE2E override the
// service the ngMockE2E version can pass through to the real backend
originalHttpBackend = $delegate.$$originalHttpBackend || $delegate;
function createResponse(status, data, headers, statusText) {
if (angular.isFunction(status)) return status;
@@ -1403,7 +1424,7 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
// if $browser specified, we do auto flush all requests
($browser ? $browser.defer : responsesPush)(wrapResponse(definition));
} else if (definition.passThrough) {
$delegate(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers);
originalHttpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers);
} else throw new Error('No response defined !');
return;
}
@@ -1879,6 +1900,8 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
responses.length = 0;
};
$httpBackend.$$originalHttpBackend = originalHttpBackend;
return $httpBackend;
@@ -2376,7 +2399,6 @@ angular.module('ngMock', ['ng']).provider({
$exceptionHandler: angular.mock.$ExceptionHandlerProvider,
$log: angular.mock.$LogProvider,
$interval: angular.mock.$IntervalProvider,
$httpBackend: angular.mock.$HttpBackendProvider,
$rootElement: angular.mock.$RootElementProvider,
$componentController: angular.mock.$ComponentControllerProvider
}).config(['$provide', '$compileProvider', function($provide, $compileProvider) {
@@ -2384,7 +2406,8 @@ angular.module('ngMock', ['ng']).provider({
$provide.decorator('$$rAF', angular.mock.$RAFDecorator);
$provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
$provide.decorator('$controller', createControllerDecorator($compileProvider));
}]);
$provide.decorator('$httpBackend', angular.mock.$httpBackendDecorator);
}]).info({ angularVersion: '"NG_VERSION_FULL"' });
/**
* @ngdoc module
@@ -2398,9 +2421,8 @@ angular.module('ngMock', ['ng']).provider({
* the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
*/
angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
$provide.value('$httpBackend', angular.injector(['ng']).get('$httpBackend'));
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
}]);
}]).info({ angularVersion: '"NG_VERSION_FULL"' });
/**
* @ngdoc service
@@ -2964,12 +2986,6 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
delete fn.$inject;
});
angular.forEach(currentSpec.$modules, function(module) {
if (module && module.$$hashKey) {
module.$$hashKey = undefined;
}
});
currentSpec.$injector = null;
currentSpec.$modules = null;
currentSpec.$providerInjector = null;
+2 -1
View File
@@ -44,4 +44,5 @@ function isValidIdentifierContinue(ch, cp) {
angular.module('ngParseExt', [])
.config(['$parseProvider', function($parseProvider) {
$parseProvider.setIdentifierFns(isValidIdentifierStart, isValidIdentifierContinue);
}]);
}])
.info({ angularVersion: '"NG_VERSION_FULL"' });
+18 -15
View File
@@ -174,7 +174,7 @@ function shallowClearAndCopy(src, dst) {
* set `transformResponse` to an empty array: `transformResponse: []`
* - **`cache`** `{boolean|Cache}` If true, a default $http cache will be used to cache the
* GET request, otherwise if a cache instance built with
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
* {@link ng.$cacheFactory $cacheFactory} is supplied, this cache will be used for
* caching.
* - **`timeout`** `{number}` timeout in milliseconds.<br />
* **Note:** In contrast to {@link ng.$http#usage $http.config}, {@link ng.$q promises} are
@@ -429,6 +429,7 @@ function shallowClearAndCopy(src, dst) {
*
*/
angular.module('ngResource', ['ng']).
info({ angularVersion: '"NG_VERSION_FULL"' }).
provider('$resource', function ResourceProvider() {
var PROTOCOL_AND_IPV6_REGEX = /^https?:\/\/\[[^\]]*][^/]*/;
@@ -590,11 +591,12 @@ angular.module('ngResource', ['ng']).
url = url.replace(/\/+$/, '') || '/';
}
// then replace collapse `/.` if found in the last URL path segment before the query
// E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
// Collapse `/.` if found in the last URL path segment before the query.
// E.g. `http://url.com/id/.format?q=x` becomes `http://url.com/id.format?q=x`.
url = url.replace(/\/\.(?=\w+($|\?))/, '.');
// replace escaped `/\.` with `/.`
config.url = protocolAndIpv6 + url.replace(/\/\\\./, '/.');
// Replace escaped `/\.` with `/.`.
// (If `\.` comes from a param value, it will be encoded as `%5C.`.)
config.url = protocolAndIpv6 + url.replace(/\/(\\|%5C)\./, '/.');
// set params - delegate param encoding to $http
@@ -636,6 +638,7 @@ angular.module('ngResource', ['ng']).
var data = extend({}, this);
delete data.$promise;
delete data.$resolved;
delete data.$cancelRequest;
return data;
};
@@ -783,18 +786,18 @@ angular.module('ngResource', ['ng']).
return value;
},
(hasError || hasResponseErrorInterceptor) ?
function(response) {
if (hasError) error(response);
return hasResponseErrorInterceptor ?
function(response) {
if (hasError && !hasResponseErrorInterceptor) {
// Avoid `Possibly Unhandled Rejection` error,
// but still fulfill the returned promise with a rejection
promise.catch(noop);
}
if (hasError) error(response);
return hasResponseErrorInterceptor ?
responseErrorInterceptor(response) :
$q.reject(response);
} :
undefined);
if (hasError && !hasResponseErrorInterceptor) {
// Avoid `Possibly Unhandled Rejection` error,
// but still fulfill the returned promise with a rejection
promise.catch(noop);
}
} :
undefined);
if (!isInstanceCall) {
// we are creating instance / collection
+14 -1
View File
@@ -7,6 +7,7 @@
var isArray;
var isObject;
var isDefined;
var noop;
/**
* @ngdoc module
@@ -26,6 +27,7 @@ var isDefined;
/* global -ngRouteModule */
var ngRouteModule = angular.
module('ngRoute', []).
info({ angularVersion: '"NG_VERSION_FULL"' }).
provider('$route', $RouteProvider).
// Ensure `$route` will be instantiated in time to capture the initial `$locationChangeSuccess`
// event (unless explicitly disabled). This is necessary in case `ngView` is included in an
@@ -54,6 +56,7 @@ function $RouteProvider() {
isArray = angular.isArray;
isObject = angular.isObject;
isDefined = angular.isDefined;
noop = angular.noop;
function inherit(parent, extra) {
return angular.extend(Object.create(parent), extra);
@@ -350,7 +353,8 @@ function $RouteProvider() {
'$injector',
'$templateRequest',
'$sce',
function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) {
'$browser',
function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce, $browser) {
/**
* @ngdoc service
@@ -680,6 +684,8 @@ function $RouteProvider() {
var nextRoutePromise = $q.resolve(nextRoute);
$browser.$$incOutstandingRequestCount();
nextRoutePromise.
then(getRedirectionData).
then(handlePossibleRedirection).
@@ -700,6 +706,13 @@ function $RouteProvider() {
if (nextRoute === $route.current) {
$rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
}
}).finally(function() {
// Because `commitRoute()` is called from a `$rootScope.$evalAsync` block (see
// `$locationWatch`), this `$$completeOutstandingRequest()` call will not cause
// `outstandingRequestCount` to hit zero. This is important in case we are redirecting
// to a new route which also requires some asynchronous work.
$browser.$$completeOutstandingRequest(noop);
});
}
}
+22 -5
View File
@@ -18,6 +18,7 @@ var forEach;
var isDefined;
var lowercase;
var noop;
var nodeContains;
var htmlParser;
var htmlSanitizeWriter;
@@ -218,6 +219,11 @@ function $SanitizeProvider() {
htmlParser = htmlParserImpl;
htmlSanitizeWriter = htmlSanitizeWriterImpl;
nodeContains = window.Node.prototype.contains || /** @this */ function(arg) {
// eslint-disable-next-line no-bitwise
return !!(this.compareDocumentPosition(arg) & 16);
};
// Regular Expressions for parsing tags and attributes
var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
// Match everything outside of normal chars and " (quote character)
@@ -381,12 +387,12 @@ function $SanitizeProvider() {
if (node.nodeType === 1) {
handler.end(node.nodeName.toLowerCase());
}
nextNode = node.nextSibling;
nextNode = getNonDescendant('nextSibling', node);
if (!nextNode) {
while (nextNode == null) {
node = node.parentNode;
node = getNonDescendant('parentNode', node);
if (node === inertBodyElement) break;
nextNode = node.nextSibling;
nextNode = getNonDescendant('nextSibling', node);
if (node.nodeType === 1) {
handler.end(node.nodeName.toLowerCase());
}
@@ -518,9 +524,18 @@ function $SanitizeProvider() {
stripCustomNsAttrs(nextNode);
}
node = node.nextSibling;
node = getNonDescendant('nextSibling', node);
}
}
function getNonDescendant(propName, node) {
// An element is clobbered if its `propName` property points to one of its descendants
var nextNode = node[propName];
if (nextNode && nodeContains.call(node, nextNode)) {
throw $sanitizeMinErr('elclob', 'Failed to sanitize html because the element is clobbered: {0}', node.outerHTML || node.outerText);
}
return nextNode;
}
}
function sanitizeText(chars) {
@@ -532,4 +547,6 @@ function sanitizeText(chars) {
// define ngSanitize module and register $sanitize service
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
angular.module('ngSanitize', [])
.provider('$sanitize', $SanitizeProvider)
.info({ angularVersion: '"NG_VERSION_FULL"' });
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* @license AngularJS v"NG_VERSION_FULL"
* (c) 2010-2016 Google, Inc. http://angularjs.org
* (c) 2010-2017 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window){
+2
View File
@@ -24,6 +24,8 @@
/* global -ngTouch */
var ngTouch = angular.module('ngTouch', []);
ngTouch.info({ angularVersion: '"NG_VERSION_FULL"' });
ngTouch.provider('$touch', $TouchProvider);
function nodeName_(element) {
+9 -3
View File
@@ -2,9 +2,15 @@
/* global toDebugString: true */
function serializeObject(obj) {
function serializeObject(obj, maxDepth) {
var seen = [];
// There is no direct way to stringify object until reaching a specific depth
// and a very deep object can cause a performance issue, so we copy the object
// based on this specific depth and then stringify it.
if (isValidObjectMaxDepth(maxDepth)) {
obj = copy(obj, null, maxDepth);
}
return JSON.stringify(obj, function(key, val) {
val = toJsonReplacer(key, val);
if (isObject(val)) {
@@ -17,13 +23,13 @@ function serializeObject(obj) {
});
}
function toDebugString(obj) {
function toDebugString(obj, maxDepth) {
if (typeof obj === 'function') {
return obj.toString().replace(/ \{[\s\S]*$/, '');
} else if (isUndefined(obj)) {
return 'undefined';
} else if (typeof obj !== 'string') {
return serializeObject(obj);
return serializeObject(obj, maxDepth);
}
return obj;
}
+4 -1
View File
@@ -25,6 +25,8 @@
/* angular.js */
"angular": false,
"minErrConfig": false,
"errorHandlingConfig": false,
"msie": false,
"jqLite": false,
"jQuery": false,
@@ -37,6 +39,7 @@
"nodeName_": false,
"uid": false,
"toDebugString": false,
"serializeObject": false,
"lowercase": false,
"uppercase": false,
@@ -143,7 +146,7 @@
/* apis.js */
"hashKey": false,
"HashMap": false,
"NgMapShim": false,
/* urlUtils.js */
"urlResolve": false,
+135 -48
View File
@@ -7,6 +7,7 @@ Float32Array, Float64Array, */
describe('angular', function() {
var element, document;
var originalObjectMaxDepthInErrorMessage = minErrConfig.objectMaxDepth;
beforeEach(function() {
document = window.document;
@@ -14,6 +15,30 @@ describe('angular', function() {
afterEach(function() {
dealoc(element);
minErrConfig.objectMaxDepth = originalObjectMaxDepthInErrorMessage;
});
describe('errorHandlingConfig', function() {
it('should get default objectMaxDepth', function() {
expect(errorHandlingConfig().objectMaxDepth).toBe(5);
});
it('should set objectMaxDepth', function() {
errorHandlingConfig({objectMaxDepth: 3});
expect(errorHandlingConfig().objectMaxDepth).toBe(3);
});
it('should not change objectMaxDepth when undefined is supplied', function() {
errorHandlingConfig({objectMaxDepth: undefined});
expect(errorHandlingConfig().objectMaxDepth).toBe(originalObjectMaxDepthInErrorMessage);
});
they('should set objectMaxDepth to NaN when $prop is supplied',
[NaN, null, true, false, -1, 0], function(maxDepth) {
errorHandlingConfig({objectMaxDepth: maxDepth});
expect(errorHandlingConfig().objectMaxDepth).toBeNaN();
}
);
});
describe('case', function() {
@@ -602,6 +627,31 @@ describe('angular', function() {
expect(copy(new Number(NaN)).valueOf()).toBeNaN();
/* eslint-enable */
});
it('should copy source until reaching a given max depth', function() {
var source = {a1: 1, b1: {b2: {b3: 1}}, c1: [1, {c2: 1}], d1: {d2: 1}};
var dest;
dest = copy(source, {}, 1);
expect(dest).toEqual({a1:1, b1:'...', c1:'...', d1:'...'});
dest = copy(source, {}, 2);
expect(dest).toEqual({a1:1, b1:{b2:'...'}, c1:[1,'...'], d1:{d2:1}});
dest = copy(source, {}, 3);
expect(dest).toEqual({a1: 1, b1: {b2: {b3: 1}}, c1: [1, {c2: 1}], d1: {d2: 1}});
dest = copy(source, {}, 4);
expect(dest).toEqual({a1: 1, b1: {b2: {b3: 1}}, c1: [1, {c2: 1}], d1: {d2: 1}});
});
they('should copy source and ignore max depth when maxDepth = $prop',
[NaN, null, undefined, true, false, -1, 0], function(maxDepth) {
var source = {a1: 1, b1: {b2: {b3: 1}}, c1: [1, {c2: 1}], d1: {d2: 1}};
var dest = copy(source, {}, maxDepth);
expect(dest).toEqual({a1: 1, b1: {b2: {b3: 1}}, c1: [1, {c2: 1}], d1: {d2: 1}});
}
);
});
describe('extend', function() {
@@ -1683,63 +1733,100 @@ describe('angular', function() {
dealoc(appElement);
});
it('should bootstrap from an extension into an extension document for same-origin documents only', function() {
if (msie) return; // IE does not support document.currentScript (nor extensions with protocol), so skip test.
// IE does not support `document.currentScript` (nor extensions with protocol), so skip tests.
if (!msie) {
describe('auto bootstrap restrictions', function() {
// Extension URLs are browser-specific, so we must choose a scheme that is supported by the browser to make
// sure that the URL is properly parsed.
var extensionScheme;
var userAgent = window.navigator.userAgent;
if (/Firefox\//.test(userAgent)) {
extensionScheme = 'moz-extension';
} else if (/Edge\//.test(userAgent)) {
extensionScheme = 'ms-browser-extension';
} else if (/Chrome\//.test(userAgent)) {
extensionScheme = 'chrome-extension';
} else if (/Safari\//.test(userAgent)) {
extensionScheme = 'safari-extension';
} else {
extensionScheme = 'browserext'; // Upcoming standard scheme.
}
function createFakeDoc(attrs, protocol, currentScript) {
var src = extensionScheme + '://something';
// Fake a minimal document object (the actual document.currentScript is readonly).
var fakeDoc = {
currentScript: { getAttribute: function() { return src; } },
location: {protocol: extensionScheme + ':', origin: extensionScheme + '://something'},
createElement: document.createElement.bind(document)
};
expect(allowAutoBootstrap(fakeDoc)).toBe(true);
protocol = protocol || 'http:';
var origin = protocol + '//something';
src = extensionScheme + '://something-else';
expect(allowAutoBootstrap(fakeDoc)).toBe(false);
});
if (currentScript === undefined) {
currentScript = document.createElement('script');
Object.keys(attrs).forEach(function(key) { currentScript.setAttribute(key, attrs[key]); });
}
it('should not bootstrap from an extension into a non-extension document', function() {
if (msie) return; // IE does not support document.currentScript (nor extensions with protocol), so skip test.
// Fake a minimal document object (the actual document.currentScript is readonly).
return {
currentScript: currentScript,
location: {protocol: protocol, origin: origin},
createElement: document.createElement.bind(document)
};
}
var src = 'resource://something';
// Fake a minimal document object (the actual document.currentScript is readonly).
var fakeDoc = {
currentScript: { getAttribute: function() { return src; } },
location: {protocol: 'http:'},
createElement: document.createElement.bind(document)
};
expect(allowAutoBootstrap(fakeDoc)).toBe(false);
it('should bootstrap from an extension into an extension document for same-origin documents only', function() {
src = 'file://whatever';
expect(allowAutoBootstrap(fakeDoc)).toBe(true);
});
// Extension URLs are browser-specific, so we must choose a scheme that is supported by the browser to make
// sure that the URL is properly parsed.
var protocol;
var userAgent = window.navigator.userAgent;
if (/Firefox\//.test(userAgent)) {
protocol = 'moz-extension:';
} else if (/Edge\//.test(userAgent)) {
protocol = 'ms-browser-extension:';
} else if (/Chrome\//.test(userAgent)) {
protocol = 'chrome-extension:';
} else if (/Safari\//.test(userAgent)) {
protocol = 'safari-extension:';
} else {
protocol = 'browserext:'; // Upcoming standard scheme.
}
it('should not bootstrap if bootstrapping is disabled', function() {
isAutoBootstrapAllowed = false;
angularInit(jqLite('<div ng-app></div>')[0], bootstrapSpy);
expect(bootstrapSpy).not.toHaveBeenCalled();
isAutoBootstrapAllowed = true;
});
expect(allowAutoBootstrap(createFakeDoc({src: protocol + '//something'}, protocol))).toBe(true);
expect(allowAutoBootstrap(createFakeDoc({src: protocol + '//something-else'}, protocol))).toBe(false);
});
it('should bootstrap from a script with no source (e.g. src, href or xlink:href attributes)', function() {
expect(allowAutoBootstrap(createFakeDoc({src: null}))).toBe(true);
expect(allowAutoBootstrap(createFakeDoc({href: null}))).toBe(true);
expect(allowAutoBootstrap(createFakeDoc({'xlink:href': null}))).toBe(true);
});
it('should not bootstrap from a script with an empty source (e.g. `src=""`)', function() {
expect(allowAutoBootstrap(createFakeDoc({src: ''}))).toBe(false);
expect(allowAutoBootstrap(createFakeDoc({href: ''}))).toBe(false);
expect(allowAutoBootstrap(createFakeDoc({'xlink:href': ''}))).toBe(false);
});
it('should not bootstrap from an extension into a non-extension document', function() {
expect(allowAutoBootstrap(createFakeDoc({src: 'resource://something'}))).toBe(false);
expect(allowAutoBootstrap(createFakeDoc({src: 'file://whatever'}))).toBe(true);
});
it('should not bootstrap from an extension into a non-extension document, via SVG script', function() {
// SVG script tags don't use the `src` attribute to load their source.
// Instead they use `href` or the deprecated `xlink:href` attributes.
expect(allowAutoBootstrap(createFakeDoc({href: 'resource://something'}))).toBe(false);
expect(allowAutoBootstrap(createFakeDoc({'xlink:href': 'resource://something'}))).toBe(false);
expect(allowAutoBootstrap(createFakeDoc({src: 'http://something', href: 'resource://something'}))).toBe(false);
expect(allowAutoBootstrap(createFakeDoc({href: 'http://something', 'xlink:href': 'resource://something'}))).toBe(false);
expect(allowAutoBootstrap(createFakeDoc({src: 'resource://something', href: 'http://something', 'xlink:href': 'http://something'}))).toBe(false);
});
it('should not bootstrap if the currentScript property has been clobbered', function() {
var img = document.createElement('img');
img.setAttribute('src', '');
expect(allowAutoBootstrap(createFakeDoc({}, 'http:', img))).toBe(false);
});
it('should not bootstrap if bootstrapping is disabled', function() {
isAutoBootstrapAllowed = false;
angularInit(jqLite('<div ng-app></div>')[0], bootstrapSpy);
expect(bootstrapSpy).not.toHaveBeenCalled();
isAutoBootstrapAllowed = true;
});
});
}
});
describe('angular service', function() {
it('should override services', function() {
module(function($provide) {
+95 -42
View File
@@ -1,56 +1,109 @@
'use strict';
describe('api', function() {
describe('hashKey()', function() {
it('should use an existing `$$hashKey`', function() {
var obj = {$$hashKey: 'foo'};
expect(hashKey(obj)).toBe('foo');
});
describe('HashMap', function() {
it('should support a function as `$$hashKey` (and call it)', function() {
var obj = {$$hashKey: valueFn('foo')};
expect(hashKey(obj)).toBe('foo');
});
it('should create a new `$$hashKey` if none exists (and return it)', function() {
var obj = {};
expect(hashKey(obj)).toBe(obj.$$hashKey);
expect(obj.$$hashKey).toBeDefined();
});
it('should create appropriate `$$hashKey`s for primitive values', function() {
expect(hashKey(undefined)).toBe(hashKey(undefined));
expect(hashKey(null)).toBe(hashKey(null));
expect(hashKey(null)).not.toBe(hashKey(undefined));
expect(hashKey(true)).toBe(hashKey(true));
expect(hashKey(false)).toBe(hashKey(false));
expect(hashKey(false)).not.toBe(hashKey(true));
expect(hashKey(42)).toBe(hashKey(42));
expect(hashKey(1337)).toBe(hashKey(1337));
expect(hashKey(1337)).not.toBe(hashKey(42));
expect(hashKey('foo')).toBe(hashKey('foo'));
expect(hashKey('foo')).not.toBe(hashKey('bar'));
});
it('should create appropriate `$$hashKey`s for non-primitive values', function() {
var fn = function() {};
var arr = [];
var obj = {};
var date = new Date();
expect(hashKey(fn)).toBe(hashKey(fn));
expect(hashKey(fn)).not.toBe(hashKey(function() {}));
expect(hashKey(arr)).toBe(hashKey(arr));
expect(hashKey(arr)).not.toBe(hashKey([]));
expect(hashKey(obj)).toBe(hashKey(obj));
expect(hashKey(obj)).not.toBe(hashKey({}));
expect(hashKey(date)).toBe(hashKey(date));
expect(hashKey(date)).not.toBe(hashKey(new Date()));
});
it('should support a custom `nextUidFn`', function() {
var nextUidFn = jasmine.createSpy('nextUidFn').and.returnValues('foo', 'bar', 'baz', 'qux');
var fn = function() {};
var arr = [];
var obj = {};
var date = new Date();
hashKey(fn, nextUidFn);
hashKey(arr, nextUidFn);
hashKey(obj, nextUidFn);
hashKey(date, nextUidFn);
expect(fn.$$hashKey).toBe('function:foo');
expect(arr.$$hashKey).toBe('object:bar');
expect(obj.$$hashKey).toBe('object:baz');
expect(date.$$hashKey).toBe('object:qux');
});
});
describe('NgMapShim', function() {
it('should do basic crud', function() {
var map = new HashMap();
var key = {};
var value1 = {};
var value2 = {};
map.put(key, value1);
map.put(key, value2);
expect(map.get(key)).toBe(value2);
expect(map.get({})).toBeUndefined();
expect(map.remove(key)).toBe(value2);
expect(map.get(key)).toBeUndefined();
var map = new NgMapShim();
var keys = [{}, {}, {}];
var values = [{}, {}, {}];
map.set(keys[0], values[1]);
map.set(keys[0], values[0]);
expect(map.get(keys[0])).toBe(values[0]);
expect(map.get(keys[1])).toBeUndefined();
map.set(keys[1], values[1]);
map.set(keys[2], values[2]);
expect(map.delete(keys[0])).toBe(true);
expect(map.delete(keys[0])).toBe(false);
expect(map.get(keys[0])).toBeUndefined();
expect(map.get(keys[1])).toBe(values[1]);
expect(map.get(keys[2])).toBe(values[2]);
});
it('should init from an array', function() {
var map = new HashMap(['a','b']);
expect(map.get('a')).toBe(0);
expect(map.get('b')).toBe(1);
expect(map.get('c')).toBeUndefined();
});
it('should be able to deal with `NaN` keys', function() {
var map = new NgMapShim();
it('should maintain hashKey for object keys', function() {
var map = new HashMap();
var key = {};
map.get(key);
expect(key.$$hashKey).toBeDefined();
});
map.set('NaN', 'foo');
map.set(NaN, 'bar');
map.set(NaN, 'baz');
it('should maintain hashKey for function keys', function() {
var map = new HashMap();
var key = function() {};
map.get(key);
expect(key.$$hashKey).toBeDefined();
});
expect(map.get('NaN')).toBe('foo');
expect(map.get(NaN)).toBe('baz');
it('should share hashKey between HashMap by default', function() {
var map1 = new HashMap(), map2 = new HashMap();
var key1 = {}, key2 = {};
map1.get(key1);
map2.get(key2);
expect(key1.$$hashKey).not.toEqual(key2.$$hashKey);
});
expect(map.delete(NaN)).toBe(true);
expect(map.get(NaN)).toBeUndefined();
expect(map.get('NaN')).toBe('foo');
it('should maintain hashKey per HashMap if flag is passed', function() {
var map1 = new HashMap([], true), map2 = new HashMap([], true);
var key1 = {}, key2 = {};
map1.get(key1);
map2.get(key2);
expect(key1.$$hashKey).toEqual(key2.$$hashKey);
expect(map.delete(NaN)).toBe(false);
});
});
});
+38 -29
View File
@@ -2,6 +2,37 @@
/* globals support: false */
describe('injector.modules', function() {
it('should expose the loaded module info on the instance injector', function() {
var test1 = angular.module('test1', ['test2']).info({ version: '1.1' });
var test2 = angular.module('test2', []).info({ version: '1.2' });
module('test1');
inject(['$injector', function($injector) {
expect(Object.keys($injector.modules)).toEqual(['ng', 'ngLocale', 'ngMock', 'test1', 'test2']);
expect($injector.modules['test1'].info()).toEqual({ version: '1.1' });
expect($injector.modules['test2'].info()).toEqual({ version: '1.2' });
}]);
});
it('should expose the loaded module info on the provider injector', function() {
var providerInjector;
var test1 = angular.module('test1', ['test2']).info({ version: '1.1' });
var test2 = angular.module('test2', [])
.info({ version: '1.2' })
.provider('test', ['$injector', function($injector) {
providerInjector = $injector;
return { $get: function() {} };
}]);
module('test1');
// needed to ensure that the provider blocks are executed
inject();
expect(Object.keys(providerInjector.modules)).toEqual(['ng', 'ngLocale', 'ngMock', 'test1', 'test2']);
expect(providerInjector.modules['test1'].info()).toEqual({ version: '1.1' });
expect(providerInjector.modules['test2'].info()).toEqual({ version: '1.2' });
});
});
describe('injector', function() {
var providers;
var injector;
@@ -46,14 +77,13 @@ describe('injector', function() {
it('should resolve dependency graph and instantiate all services just once', function() {
var log = [];
// s1
// / | \
// / s2 \
// / / | \ \
// /s3 < s4 > s5
// //
// s6
// s1
// / | \
// / s2 \
// / / | \ \
// /s3 < s4 > s5
// //
// s6
providers('s1', function() { log.push('s1'); return {}; }, {$inject: ['s2', 's5', 's6']});
providers('s2', function() { log.push('s2'); return {}; }, {$inject: ['s3', 's4', 's5']});
@@ -285,14 +315,6 @@ describe('injector', function() {
// eslint-disable-next-line no-eval
expect(annotate(eval('a => b => b'))).toEqual(['a']);
});
// Support: Chrome 50-51 only
// TODO (gkalpak): Remove when Chrome v52 is released.
// it('should be able to inject fat-arrow function', function() {
// inject(($injector) => {
// expect($injector).toBeDefined();
// });
// });
}
if (support.classes) {
@@ -325,19 +347,6 @@ describe('injector', function() {
expect(instance).toEqual(jasmine.any(Clazz));
});
}
// Support: Chrome 50-51 only
// TODO (gkalpak): Remove when Chrome v52 is released.
// it('should be able to invoke classes', function() {
// class Test {
// constructor($injector) {
// this.$injector = $injector;
// }
// }
// var instance = injector.invoke(Test, null, null, 'Test');
// expect(instance.$injector).toBe(injector);
// });
}
});
@@ -0,0 +1,9 @@
<html ng-app="lettersApp">
<body>
<div ng-view></div>
<script src="angular.js"></script>
<script src="angular-route.js"></script>
<script src="script.js"></script>
</body>
</html>

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