Compare commits

...

29 Commits

Author SHA1 Message Date
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
29 changed files with 2229 additions and 1131 deletions
+245 -21
View File
@@ -1,3 +1,130 @@
<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,9 +135,15 @@ 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))
@@ -38,6 +171,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 +187,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 +206,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 +239,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 +280,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 +306,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 +328,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 +372,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 +394,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))
@@ -1595,6 +1771,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 +1781,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 +1818,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))
@@ -1637,21 +1831,46 @@ Please read the [Sandbox Removal Blog Post](http://angularjs.blogspot.com/2016/0
- 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 +1879,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))
@@ -4273,6 +4496,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
@@ -14710,7 +14934,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
+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>
+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);
};
}]
};
}])
+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 -1
View File
@@ -88,7 +88,8 @@ 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.
- 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.
+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 />
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "angularjs",
"license": "MIT",
"branchVersion": "^1.5.8",
"branchVersion": "^1.6.0",
"branchPattern": "1.6.*",
"distTag": "latest",
"repository": {
@@ -26,7 +26,7 @@
"browserstacktunnel-wrapper": "^1.4.2",
"canonical-path": "0.0.2",
"changez": "^2.1.1",
"changez-angular": "^2.1.0",
"changez-angular": "^2.1.2",
"cheerio": "^0.17.0",
"commitizen": "^2.3.0",
"cross-spawn": "^4.0.0",
+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;
}
}
},
+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;
+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;
}
}
/**
+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
};
};
+18 -10
View File
@@ -505,17 +505,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 +605,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;
+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.
+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);
}
}
}
}
+10 -5
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 = [],
@@ -798,10 +803,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) {
+2 -4
View File
@@ -629,10 +629,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
+1 -2
View File
@@ -391,8 +391,7 @@ function generateInputCompilerHelper(helper) {
};
helper.changeInputValueTo = function(value) {
helper.inputElm.val(value);
browserTrigger(helper.inputElm, $sniffer.hasEvent('input') ? 'input' : 'change');
helper.changeGivenInputTo(helper.inputElm, value);
};
helper.changeGivenInputTo = function(inputElm, value) {
+8
View File
@@ -2182,6 +2182,14 @@ describe('jqLite', function() {
span.after('abc');
expect(root.html().toLowerCase()).toEqual('<span></span>abc');
});
it('should not throw when the element has no parent', function() {
var span = jqLite('<span></span>');
expect(function() { span.after('abc'); }).not.toThrow();
expect(span.length).toBe(1);
expect(span[0].outerHTML).toBe('<span></span>');
});
});
+118 -86
View File
@@ -3,103 +3,135 @@
describe('$$cookieReader', function() {
var $$cookieReader, document;
function deleteAllCookies() {
var cookies = document.cookie.split(';');
var path = window.location.pathname;
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i];
var eqPos = cookie.indexOf('=');
var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
var parts = path.split('/');
while (parts.length) {
document.cookie = name + '=;path=' + (parts.join('/') || '/') + ';expires=Thu, 01 Jan 1970 00:00:00 GMT';
parts.pop();
describe('with access to `document.cookie`', function() {
function deleteAllCookies() {
var cookies = document.cookie.split(';');
var path = window.location.pathname;
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i];
var eqPos = cookie.indexOf('=');
var name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
var parts = path.split('/');
while (parts.length) {
document.cookie = name + '=;path=' + (parts.join('/') || '/') + ';expires=Thu, 01 Jan 1970 00:00:00 GMT';
parts.pop();
}
}
}
}
beforeEach(function() {
document = window.document;
deleteAllCookies();
expect(document.cookie).toEqual('');
beforeEach(function() {
document = window.document;
deleteAllCookies();
expect(document.cookie).toEqual('');
inject(function(_$$cookieReader_) {
inject(function(_$$cookieReader_) {
$$cookieReader = _$$cookieReader_;
});
});
afterEach(function() {
deleteAllCookies();
expect(document.cookie).toEqual('');
});
describe('get via $$cookieReader()[cookieName]', function() {
it('should return undefined for nonexistent cookie', function() {
expect($$cookieReader().nonexistent).not.toBeDefined();
});
it('should return a value for an existing cookie', function() {
document.cookie = 'foo=bar=baz;path=/';
expect($$cookieReader().foo).toEqual('bar=baz');
});
it('should return the the first value provided for a cookie', function() {
// For a cookie that has different values that differ by path, the
// value for the most specific path appears first. $$cookieReader()
// should provide that value for the cookie.
document.cookie = 'foo="first"; foo="second"';
expect($$cookieReader()['foo']).toBe('"first"');
});
it('should decode cookie values that were encoded by puts', function() {
document.cookie = 'cookie2%3Dbar%3Bbaz=val%3Due;path=/';
expect($$cookieReader()['cookie2=bar;baz']).toEqual('val=ue');
});
it('should preserve leading & trailing spaces in names and values', function() {
document.cookie = '%20cookie%20name%20=%20cookie%20value%20';
expect($$cookieReader()[' cookie name ']).toEqual(' cookie value ');
expect($$cookieReader()['cookie name']).not.toBeDefined();
});
it('should decode special characters in cookie values', function() {
document.cookie = 'cookie_name=cookie_value_%E2%82%AC';
expect($$cookieReader()['cookie_name']).toEqual('cookie_value_€');
});
it('should not decode cookie values that do not appear to be encoded', function() {
// see #9211 - sometimes cookies contain a value that causes decodeURIComponent to throw
document.cookie = 'cookie_name=cookie_value_%XX';
expect($$cookieReader()['cookie_name']).toEqual('cookie_value_%XX');
});
});
describe('getAll via $$cookieReader()', function() {
it('should return cookies as hash', function() {
document.cookie = 'foo1=bar1;path=/';
document.cookie = 'foo2=bar2;path=/';
expect($$cookieReader()).toEqual({'foo1':'bar1', 'foo2':'bar2'});
});
it('should return empty hash if no cookies exist', function() {
expect($$cookieReader()).toEqual({});
});
});
it('should initialize cookie cache with existing cookies', function() {
document.cookie = 'existingCookie=existingValue;path=/';
expect($$cookieReader()).toEqual({'existingCookie':'existingValue'});
});
});
describe('without access to `document.cookie`', function() {
var cookieSpy;
beforeEach(module(function($provide) {
cookieSpy = jasmine.createSpy('cookie').and.throwError('Can\'t touch this!');
document = Object.create({}, {'cookie': {get: cookieSpy}});
$provide.value('$document', [document]);
}));
beforeEach(inject(function(_$$cookieReader_) {
$$cookieReader = _$$cookieReader_;
});
});
}));
afterEach(function() {
deleteAllCookies();
expect(document.cookie).toEqual('');
});
describe('get via $$cookieReader()[cookieName]', function() {
it('should return undefined for nonexistent cookie', function() {
expect($$cookieReader().nonexistent).not.toBeDefined();
});
it('should return a value for an existing cookie', function() {
document.cookie = 'foo=bar=baz;path=/';
expect($$cookieReader().foo).toEqual('bar=baz');
});
it('should return the the first value provided for a cookie', function() {
// For a cookie that has different values that differ by path, the
// value for the most specific path appears first. $$cookieReader()
// should provide that value for the cookie.
document.cookie = 'foo="first"; foo="second"';
expect($$cookieReader()['foo']).toBe('"first"');
});
it('should decode cookie values that were encoded by puts', function() {
document.cookie = 'cookie2%3Dbar%3Bbaz=val%3Due;path=/';
expect($$cookieReader()['cookie2=bar;baz']).toEqual('val=ue');
});
it('should preserve leading & trailing spaces in names and values', function() {
document.cookie = '%20cookie%20name%20=%20cookie%20value%20';
expect($$cookieReader()[' cookie name ']).toEqual(' cookie value ');
expect($$cookieReader()['cookie name']).not.toBeDefined();
});
it('should decode special characters in cookie values', function() {
document.cookie = 'cookie_name=cookie_value_%E2%82%AC';
expect($$cookieReader()['cookie_name']).toEqual('cookie_value_€');
});
it('should not decode cookie values that do not appear to be encoded', function() {
// see #9211 - sometimes cookies contain a value that causes decodeURIComponent to throw
document.cookie = 'cookie_name=cookie_value_%XX';
expect($$cookieReader()['cookie_name']).toEqual('cookie_value_%XX');
});
});
describe('getAll via $$cookieReader()', function() {
it('should return cookies as hash', function() {
document.cookie = 'foo1=bar1;path=/';
document.cookie = 'foo2=bar2;path=/';
expect($$cookieReader()).toEqual({'foo1':'bar1', 'foo2':'bar2'});
});
it('should return empty hash if no cookies exist', function() {
it('should return an empty object', function() {
expect($$cookieReader()).toEqual({});
expect(cookieSpy).toHaveBeenCalled();
});
});
it('should initialize cookie cache with existing cookies', function() {
document.cookie = 'existingCookie=existingValue;path=/';
expect($$cookieReader()).toEqual({'existingCookie':'existingValue'});
});
});
+254 -42
View File
@@ -244,21 +244,34 @@ describe('ngClass', function() {
}));
it('should allow ngClassOdd/Even on the same element with overlapping classes', inject(function($rootScope, $compile, $animate) {
var className;
element = $compile('<ul><li ng-repeat="i in [0,1,2]" ng-class-odd="\'same odd\'" ng-class-even="\'same even\'"></li><ul>')($rootScope);
it('should allow ngClassOdd/Even on the same element with overlapping classes',
inject(function($compile, $rootScope) {
element = $compile(
'<ul>' +
'<li ng-repeat="i in [0,1,2]" ' +
'ng-class-odd="\'same odd\'" ' +
'ng-class-even="\'same even\'">' +
'</li>' +
'<ul>')($rootScope);
$rootScope.$digest();
var e1 = jqLite(element[0].childNodes[1]);
var e2 = jqLite(element[0].childNodes[5]);
expect(e1.hasClass('same')).toBeTruthy();
expect(e1.hasClass('odd')).toBeTruthy();
expect(e2.hasClass('same')).toBeTruthy();
expect(e2.hasClass('odd')).toBeTruthy();
var e1 = element.children().eq(0);
var e2 = element.children().eq(1);
var e3 = element.children().eq(2);
expect(e1).toHaveClass('same');
expect(e1).toHaveClass('odd');
expect(e1).not.toHaveClass('even');
expect(e2).toHaveClass('same');
expect(e2).not.toHaveClass('odd');
expect(e2).toHaveClass('even');
expect(e3).toHaveClass('same');
expect(e3).toHaveClass('odd');
expect(e3).not.toHaveClass('even');
})
);
it('should allow ngClass with overlapping classes', inject(function($rootScope, $compile, $animate) {
it('should allow ngClass with overlapping classes', inject(function($rootScope, $compile) {
element = $compile('<div ng-class="{\'same yes\': test, \'same no\': !test}"></div>')($rootScope);
$rootScope.$digest();
@@ -266,9 +279,7 @@ describe('ngClass', function() {
expect(element).not.toHaveClass('yes');
expect(element).toHaveClass('no');
$rootScope.$apply(function() {
$rootScope.test = true;
});
$rootScope.$apply('test = true');
expect(element).toHaveClass('same');
expect(element).toHaveClass('yes');
@@ -299,38 +310,79 @@ describe('ngClass', function() {
expect(e2.hasClass('D')).toBeFalsy();
}));
it('should reapply ngClass when interpolated class attribute changes',
inject(function($compile, $rootScope) {
element = $compile(
'<div>' +
'<div class="one {{two}} three" ng-class="{five: five}"></div>' +
'<div class="one {{two}} three {{four}}" ng-class="{five: five}"></div>' +
'</div>')($rootScope);
var e1 = element.children().eq(0);
var e2 = element.children().eq(1);
it('should reapply ngClass when interpolated class attribute changes', inject(function($rootScope, $compile) {
element = $compile('<div class="one {{cls}} three" ng-class="{four: four}"></div>')($rootScope);
$rootScope.$apply('two = "two"; five = true');
$rootScope.$apply(function() {
$rootScope.cls = 'two';
$rootScope.four = true;
});
expect(element).toHaveClass('one');
expect(element).toHaveClass('two'); // interpolated
expect(element).toHaveClass('three');
expect(element).toHaveClass('four');
expect(e1).toHaveClass('one');
expect(e1).toHaveClass('two');
expect(e1).toHaveClass('three');
expect(e1).not.toHaveClass('four');
expect(e1).toHaveClass('five');
expect(e2).toHaveClass('one');
expect(e2).toHaveClass('two');
expect(e2).toHaveClass('three');
expect(e2).not.toHaveClass('four');
expect(e2).toHaveClass('five');
$rootScope.$apply(function() {
$rootScope.cls = 'too';
});
expect(element).toHaveClass('one');
expect(element).toHaveClass('too'); // interpolated
expect(element).toHaveClass('three');
expect(element).toHaveClass('four'); // should still be there
expect(element.hasClass('two')).toBeFalsy();
$rootScope.$apply('two = "another-two"');
$rootScope.$apply(function() {
$rootScope.cls = 'to';
});
expect(element).toHaveClass('one');
expect(element).toHaveClass('to'); // interpolated
expect(element).toHaveClass('three');
expect(element).toHaveClass('four'); // should still be there
expect(element.hasClass('two')).toBeFalsy();
expect(element.hasClass('too')).toBeFalsy();
}));
expect(e1).toHaveClass('one');
expect(e1).not.toHaveClass('two');
expect(e1).toHaveClass('another-two');
expect(e1).toHaveClass('three');
expect(e1).not.toHaveClass('four');
expect(e1).toHaveClass('five');
expect(e2).toHaveClass('one');
expect(e2).not.toHaveClass('two');
expect(e2).toHaveClass('another-two');
expect(e2).toHaveClass('three');
expect(e2).not.toHaveClass('four');
expect(e2).toHaveClass('five');
$rootScope.$apply('two = "two-more"; four = "four"');
expect(e1).toHaveClass('one');
expect(e1).not.toHaveClass('two');
expect(e1).not.toHaveClass('another-two');
expect(e1).toHaveClass('two-more');
expect(e1).toHaveClass('three');
expect(e1).not.toHaveClass('four');
expect(e1).toHaveClass('five');
expect(e2).toHaveClass('one');
expect(e2).not.toHaveClass('two');
expect(e2).not.toHaveClass('another-two');
expect(e2).toHaveClass('two-more');
expect(e2).toHaveClass('three');
expect(e2).toHaveClass('four');
expect(e2).toHaveClass('five');
$rootScope.$apply('five = false');
expect(e1).toHaveClass('one');
expect(e1).not.toHaveClass('two');
expect(e1).not.toHaveClass('another-two');
expect(e1).toHaveClass('two-more');
expect(e1).toHaveClass('three');
expect(e1).not.toHaveClass('four');
expect(e1).not.toHaveClass('five');
expect(e2).toHaveClass('one');
expect(e2).not.toHaveClass('two');
expect(e2).not.toHaveClass('another-two');
expect(e2).toHaveClass('two-more');
expect(e2).toHaveClass('three');
expect(e2).toHaveClass('four');
expect(e2).not.toHaveClass('five');
})
);
it('should not mess up class value due to observing an interpolated class attribute', inject(function($rootScope, $compile) {
@@ -409,6 +461,47 @@ describe('ngClass', function() {
expect(e2.hasClass('odd')).toBeFalsy();
}));
it('should add/remove the correct classes when the expression and `$index` change simultaneously',
inject(function($compile, $rootScope) {
element = $compile(
'<div>' +
'<div ng-class-odd="foo"></div>' +
'<div ng-class-even="foo"></div>' +
'</div>')($rootScope);
var odd = element.children().eq(0);
var even = element.children().eq(1);
$rootScope.$apply('$index = 0; foo = "class1"');
expect(odd).toHaveClass('class1');
expect(odd).not.toHaveClass('class2');
expect(even).not.toHaveClass('class1');
expect(even).not.toHaveClass('class2');
$rootScope.$apply('$index = 1; foo = "class2"');
expect(odd).not.toHaveClass('class1');
expect(odd).not.toHaveClass('class2');
expect(even).not.toHaveClass('class1');
expect(even).toHaveClass('class2');
$rootScope.$apply('foo = "class1"');
expect(odd).not.toHaveClass('class1');
expect(odd).not.toHaveClass('class2');
expect(even).toHaveClass('class1');
expect(even).not.toHaveClass('class2');
$rootScope.$apply('$index = 2');
expect(odd).toHaveClass('class1');
expect(odd).not.toHaveClass('class2');
expect(even).not.toHaveClass('class1');
expect(even).not.toHaveClass('class2');
})
);
it('should support mixed array/object variable with a mutating object',
inject(function($rootScope, $compile) {
element = $compile('<div ng-class="classVar"></div>')($rootScope);
@@ -424,6 +517,125 @@ describe('ngClass', function() {
})
);
it('should do value stabilization as expected when one-time binding',
inject(function($rootScope, $compile) {
element = $compile('<div ng-class="::className"></div>')($rootScope);
$rootScope.$apply('className = "foo"');
expect(element).toHaveClass('foo');
$rootScope.$apply('className = "bar"');
expect(element).toHaveClass('foo');
})
);
it('should remove the watcher when static array one-time binding',
inject(function($rootScope, $compile) {
element = $compile('<div ng-class="::[className]"></div>')($rootScope);
$rootScope.$apply('className = "foo"');
expect(element).toHaveClass('foo');
$rootScope.$apply('className = "bar"');
expect(element).toHaveClass('foo');
expect(element).not.toHaveClass('bar');
})
);
it('should remove the watcher when static map one-time binding',
inject(function($rootScope, $compile) {
element = $compile('<div ng-class="::{foo: fooPresent}"></div>')($rootScope);
$rootScope.$apply('fooPresent = true');
expect(element).toHaveClass('foo');
$rootScope.$apply('fooPresent = false');
expect(element).toHaveClass('foo');
})
);
it('should track changes of mutating object inside an array',
inject(function($rootScope, $compile) {
$rootScope.classVar = [{orange: true}];
element = $compile('<div ng-class="classVar"></div>')($rootScope);
$rootScope.$digest();
expect(element).toHaveClass('orange');
$rootScope.$apply('classVar[0].orange = false');
expect(element).not.toHaveClass('orange');
})
);
describe('large objects', function() {
var getProp;
var veryLargeObj;
beforeEach(function() {
getProp = jasmine.createSpy('getProp');
veryLargeObj = {};
Object.defineProperty(veryLargeObj, 'prop', {
get: getProp,
enumerable: true
});
});
it('should not be copied when using an expression', inject(function($compile, $rootScope) {
element = $compile('<div ng-class="fooClass"></div>')($rootScope);
$rootScope.fooClass = {foo: veryLargeObj};
$rootScope.$digest();
expect(element).toHaveClass('foo');
expect(getProp).not.toHaveBeenCalled();
}));
it('should not be copied when using a literal', inject(function($compile, $rootScope) {
element = $compile('<div ng-class="{foo: veryLargeObj}"></div>')($rootScope);
$rootScope.veryLargeObj = veryLargeObj;
$rootScope.$digest();
expect(element).toHaveClass('foo');
expect(getProp).not.toHaveBeenCalled();
}));
it('should not be copied when inside an array', inject(function($compile, $rootScope) {
element = $compile('<div ng-class="[{foo: veryLargeObj}]"></div>')($rootScope);
$rootScope.veryLargeObj = veryLargeObj;
$rootScope.$digest();
expect(element).toHaveClass('foo');
expect(getProp).not.toHaveBeenCalled();
}));
it('should not be copied when using one-time binding', inject(function($compile, $rootScope) {
element = $compile('<div ng-class="::{foo: veryLargeObj, bar: bar}"></div>')($rootScope);
$rootScope.veryLargeObj = veryLargeObj;
$rootScope.$digest();
expect(element).toHaveClass('foo');
expect(element).not.toHaveClass('bar');
expect(getProp).not.toHaveBeenCalled();
$rootScope.$apply('veryLargeObj.bar = "bar"');
expect(element).toHaveClass('foo');
expect(element).not.toHaveClass('bar');
expect(getProp).not.toHaveBeenCalled();
$rootScope.$apply('bar = "bar"');
expect(element).toHaveClass('foo');
expect(element).toHaveClass('bar');
expect(getProp).not.toHaveBeenCalled();
$rootScope.$apply('veryLargeObj.bar = "qux"');
expect(element).toHaveClass('foo');
expect(element).toHaveClass('bar');
expect(getProp).not.toHaveBeenCalled();
}));
});
});
describe('ngClass animations', function() {
File diff suppressed because it is too large Load Diff
+81
View File
@@ -2827,6 +2827,7 @@ describe('ngOptions', function() {
expect(scope.selected).toEqual(['0']);
});
it('should deselect all options when model is emptied', function() {
createMultiSelect();
scope.$apply(function() {
@@ -2841,6 +2842,86 @@ describe('ngOptions', function() {
expect(element.find('option')[0].selected).toEqual(false);
});
it('should not re-set the `selected` property if it already has the correct value', function() {
scope.values = [{name: 'A'}, {name: 'B'}];
createMultiSelect();
var options = element.find('option');
var optionsSetSelected = [];
var _selected = [];
// Set up spies
forEach(options, function(option, i) {
optionsSetSelected[i] = jasmine.createSpy('optionSetSelected' + i);
_selected[i] = option.selected;
Object.defineProperty(option, 'selected', {
get: function() { return _selected[i]; },
set: optionsSetSelected[i].and.callFake(function(value) { _selected[i] = value; })
});
});
// Select `optionA`
scope.$apply('selected = [values[0]]');
expect(optionsSetSelected[0]).toHaveBeenCalledOnceWith(true);
expect(optionsSetSelected[1]).not.toHaveBeenCalled();
expect(options[0].selected).toBe(true);
expect(options[1].selected).toBe(false);
optionsSetSelected[0].calls.reset();
optionsSetSelected[1].calls.reset();
// Select `optionB` (`optionA` remains selected)
scope.$apply('selected.push(values[1])');
expect(optionsSetSelected[0]).not.toHaveBeenCalled();
expect(optionsSetSelected[1]).toHaveBeenCalledOnceWith(true);
expect(options[0].selected).toBe(true);
expect(options[1].selected).toBe(true);
optionsSetSelected[0].calls.reset();
optionsSetSelected[1].calls.reset();
// Unselect `optionA` (`optionB` remains selected)
scope.$apply('selected.shift()');
expect(optionsSetSelected[0]).toHaveBeenCalledOnceWith(false);
expect(optionsSetSelected[1]).not.toHaveBeenCalled();
expect(options[0].selected).toBe(false);
expect(options[1].selected).toBe(true);
optionsSetSelected[0].calls.reset();
optionsSetSelected[1].calls.reset();
// Reselect `optionA` (`optionB` remains selected)
scope.$apply('selected.push(values[0])');
expect(optionsSetSelected[0]).toHaveBeenCalledOnceWith(true);
expect(optionsSetSelected[1]).not.toHaveBeenCalled();
expect(options[0].selected).toBe(true);
expect(options[1].selected).toBe(true);
optionsSetSelected[0].calls.reset();
optionsSetSelected[1].calls.reset();
// Unselect `optionB` (`optionA` remains selected)
scope.$apply('selected.shift()');
expect(optionsSetSelected[0]).not.toHaveBeenCalled();
expect(optionsSetSelected[1]).toHaveBeenCalledOnceWith(false);
expect(options[0].selected).toBe(true);
expect(options[1].selected).toBe(false);
optionsSetSelected[0].calls.reset();
optionsSetSelected[1].calls.reset();
// Unselect `optionA`
scope.$apply('selected.length = 0');
expect(optionsSetSelected[0]).toHaveBeenCalledOnceWith(false);
expect(optionsSetSelected[1]).not.toHaveBeenCalled();
expect(options[0].selected).toBe(false);
expect(options[1].selected).toBe(false);
optionsSetSelected[0].calls.reset();
optionsSetSelected[1].calls.reset();
});
});
+90 -39
View File
@@ -181,16 +181,14 @@ describe('q', function() {
};
function exceptionHandler(reason) {
exceptionHandlerCalls.push(reason);
function exceptionHandler(exception, reason) {
if (typeof reason === 'undefined') {
exceptionHandlerCalls.push({ reason: exception });
} else {
exceptionHandlerCalls.push({ reason: reason, exception: exception });
}
}
function exceptionHandlerStr() {
return exceptionHandlerCalls.join('; ');
}
beforeEach(function() {
q = qFactory(mockNextTick.nextTick, exceptionHandler, true);
q_no_error = qFactory(mockNextTick.nextTick, exceptionHandler, false);
@@ -2167,45 +2165,98 @@ describe('q', function() {
describe('when exceptionHandler is called', function() {
it('should log an unhandled rejected promise', function() {
var defer = q.defer();
defer.reject('foo');
mockNextTick.flush();
expect(exceptionHandlerStr()).toBe('Possibly unhandled rejection: foo');
});
function CustomError() { }
CustomError.prototype = Object.create(Error.prototype);
var errorEg = new Error('Fail');
var errorStr = toDebugString(errorEg);
var customError = new CustomError('Custom');
var customErrorStr = toDebugString(customError);
var nonErrorObj = { isATest: 'this is' };
var nonErrorObjStr = toDebugString(nonErrorObj);
var fixtures = [
{
type: 'Error object',
value: errorEg,
expected: {
exception: errorEg,
reason: 'Possibly unhandled rejection: ' + errorStr
}
},
{
type: 'custom Error object',
value: customError,
expected: {
exception: customError,
reason: 'Possibly unhandled rejection: ' + customErrorStr
}
},
{
type: 'non-Error object',
value: nonErrorObj,
expected: {
reason: 'Possibly unhandled rejection: ' + nonErrorObjStr
}
},
{
type: 'string primitive',
value: 'foo',
expected: {
reason: 'Possibly unhandled rejection: foo'
}
}
];
forEach(fixtures, function(fixture) {
var type = fixture.type;
var value = fixture.value;
var expected = fixture.expected;
describe('with ' + type, function() {
it('should log an unhandled rejected promise', function() {
var defer = q.defer();
defer.reject(value);
mockNextTick.flush();
expect(exceptionHandlerCalls).toEqual([expected]);
});
it('should not log an unhandled rejected promise if disabled', function() {
var defer = q_no_error.defer();
defer.reject('foo');
expect(exceptionHandlerStr()).toBe('');
});
it('should not log an unhandled rejected promise if disabled', function() {
var defer = q_no_error.defer();
defer.reject(value);
expect(exceptionHandlerCalls).toEqual([]);
});
it('should log a handled rejected promise on a promise without rejection callbacks', function() {
var defer = q.defer();
defer.promise.then(noop);
defer.reject('foo');
mockNextTick.flush();
expect(exceptionHandlerStr()).toBe('Possibly unhandled rejection: foo');
});
it('should log a handled rejected promise on a promise without rejection callbacks', function() {
var defer = q.defer();
defer.promise.then(noop);
defer.reject(value);
mockNextTick.flush();
expect(exceptionHandlerCalls).toEqual([expected]);
});
it('should not log a handled rejected promise', function() {
var defer = q.defer();
defer.promise.catch(noop);
defer.reject('foo');
mockNextTick.flush();
expect(exceptionHandlerStr()).toBe('');
});
it('should not log a handled rejected promise', function() {
var defer = q.defer();
defer.promise.catch(noop);
defer.reject(value);
mockNextTick.flush();
expect(exceptionHandlerCalls).toEqual([]);
});
it('should not log a handled rejected promise that is handled in a future tick', function() {
var defer = q.defer();
defer.promise.catch(noop);
defer.resolve(q.reject('foo'));
mockNextTick.flush();
expect(exceptionHandlerStr()).toBe('');
it('should not log a handled rejected promise that is handled in a future tick', function() {
var defer = q.defer();
defer.promise.catch(noop);
defer.resolve(q.reject(value));
mockNextTick.flush();
expect(exceptionHandlerCalls).toEqual([]);
});
});
});
});
});
+74 -3
View File
@@ -498,6 +498,78 @@ describe('Scope', function() {
expect(watch2).toHaveBeenCalled();
}));
it('should not skip watchers when adding new watchers during digest',
inject(function($rootScope) {
var log = [];
var watchFn1 = function() { log.push(1); };
var watchFn2 = function() { log.push(2); };
var watchFn3 = function() { log.push(3); };
var addWatcherOnce = function(newValue, oldValue) {
if (newValue === oldValue) {
$rootScope.$watch(watchFn3);
}
};
$rootScope.$watch(watchFn1, addWatcherOnce);
$rootScope.$watch(watchFn2);
$rootScope.$digest();
expect(log).toEqual([1, 2, 3, 1, 2, 3]);
})
);
it('should not run the current watcher twice when removing a watcher during digest',
inject(function($rootScope) {
var log = [];
var removeWatcher3;
var watchFn3 = function() { log.push(3); };
var watchFn2 = function() { log.push(2); };
var watchFn1 = function() { log.push(1); };
var removeWatcherOnce = function(newValue, oldValue) {
if (newValue === oldValue) {
removeWatcher3();
}
};
$rootScope.$watch(watchFn1, removeWatcherOnce);
$rootScope.$watch(watchFn2);
removeWatcher3 = $rootScope.$watch(watchFn3);
$rootScope.$digest();
expect(log).toEqual([1, 2, 1, 2]);
})
);
it('should not skip watchers when removing itself during digest',
inject(function($rootScope) {
var log = [];
var removeWatcher1;
var watchFn3 = function() { log.push(3); };
var watchFn2 = function() { log.push(2); };
var watchFn1 = function() { log.push(1); };
var removeItself = function() {
removeWatcher1();
};
removeWatcher1 = $rootScope.$watch(watchFn1, removeItself);
$rootScope.$watch(watchFn2);
$rootScope.$watch(watchFn3);
$rootScope.$digest();
expect(log).toEqual([1, 2, 3, 2, 3]);
})
);
it('should not infinitely digest when current value is NaN', inject(function($rootScope) {
$rootScope.$watch(function() { return NaN;});
@@ -598,7 +670,7 @@ describe('Scope', function() {
$rootScope.$digest();
expect(log).toEqual(['watch1', 'watchAction1', 'watch1', 'watch3', 'watchAction3',
expect(log).toEqual(['watch1', 'watchAction1', 'watch3', 'watchAction3',
'watch1', 'watch3']);
scope.$destroy();
log.reset();
@@ -895,8 +967,7 @@ describe('Scope', function() {
$rootScope.$watch(log.fn('w5'), log.fn('w5action'));
});
$rootScope.$digest();
expect(log).toEqual(['w1', 'w2', 'w3', 'w4', 'w4action',
'w1', 'w2', 'w3', 'w4', 'w5', 'w5action',
expect(log).toEqual(['w1', 'w2', 'w3', 'w4', 'w4action', 'w5', 'w5action',
'w1', 'w2', 'w3', 'w4', 'w5']);
}));
+22 -22
View File
@@ -947,9 +947,9 @@ change-case@3.0.0:
upper-case "^1.1.1"
upper-case-first "^1.1.0"
changez-angular@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/changez-angular/-/changez-angular-2.1.0.tgz#927f7f5b7227eadabed79e1b2c0924c83984f7a3"
changez-angular@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/changez-angular/-/changez-angular-2.1.2.tgz#9f32fc5439a949eee426087dd5d108bc4bf6de23"
dependencies:
changez "^2.1.0"
nunjucks-date "^1.2.0"
@@ -2468,7 +2468,16 @@ glob@7.0.5, glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@~7.0.0:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^4.0.5, glob@^4.3.1, glob@~4.3.0:
glob@^4.0.5, glob@~4.0.2:
version "4.0.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-4.0.6.tgz#695c50bdd4e2fb5c5d370b091f388d3707e291a7"
dependencies:
graceful-fs "^3.0.2"
inherits "2"
minimatch "^1.0.0"
once "^1.3.0"
glob@^4.3.1, glob@~4.3.0:
version "4.3.5"
resolved "https://registry.yarnpkg.com/glob/-/glob-4.3.5.tgz#80fbb08ca540f238acce5d11d1e9bc41e75173d3"
dependencies:
@@ -2516,15 +2525,6 @@ glob@~3.1.21:
inherits "1"
minimatch "~0.2.11"
glob@~4.0.2:
version "4.0.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-4.0.6.tgz#695c50bdd4e2fb5c5d370b091f388d3707e291a7"
dependencies:
graceful-fs "^3.0.2"
inherits "2"
minimatch "^1.0.0"
once "^1.3.0"
global-modules@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d"
@@ -3939,11 +3939,11 @@ lodash@3.10.1, lodash@^3.10.1, lodash@^3.8.0, lodash@~3.10.1:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
lodash@4.15.0, lodash@^4.0.0, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.7.0, lodash@^4.8.0:
lodash@4.15.0, lodash@^4.0.0, lodash@^4.13.1, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.7.0, lodash@^4.8.0:
version "4.15.0"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.15.0.tgz#3162391d8f0140aa22cf8f6b3c34d6b7f63d3aa9"
lodash@4.16.2:
lodash@4.16.2, lodash@^4.14.0:
version "4.16.2"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.16.2.tgz#3e626db827048a699281a8a125226326cfc0e652"
@@ -4963,18 +4963,18 @@ pump@^0.3.5:
end-of-stream "~1.0.0"
once "~1.2.0"
punycode@1.3.2:
punycode@1.3.2, punycode@>=0.2.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
punycode@>=0.2.0, punycode@~1.2.3:
version "1.2.4"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.2.4.tgz#54008ac972aec74175def9cba6df7fa9d3918740"
punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
punycode@~1.2.3:
version "1.2.4"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.2.4.tgz#54008ac972aec74175def9cba6df7fa9d3918740"
q-io@^1.10.9, q-io@~1.13.2:
version "1.13.2"
resolved "https://registry.yarnpkg.com/q-io/-/q-io-1.13.2.tgz#eea130d481ddb5e1aa1bc5a66855f7391d06f003"
@@ -5104,7 +5104,7 @@ read@~1.0.4:
dependencies:
mute-stream "~0.0.4"
readable-stream@1.1, "readable-stream@>=1.1.13-1 <1.2.0-0", readable-stream@^1.0.27-1, readable-stream@^1.0.33-1, readable-stream@^1.1.13, readable-stream@^1.1.13-1, readable-stream@~1.1.8, readable-stream@~1.1.9:
readable-stream@1.1, "readable-stream@>=1.1.13-1 <1.2.0-0", readable-stream@^1.0.27-1, readable-stream@^1.1.13, readable-stream@^1.1.13-1, readable-stream@~1.1.8, readable-stream@~1.1.9:
version "1.1.13"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e"
dependencies:
@@ -5113,7 +5113,7 @@ readable-stream@1.1, "readable-stream@>=1.1.13-1 <1.2.0-0", readable-stream@^1.0
isarray "0.0.1"
string_decoder "~0.10.x"
"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@~1.0.0, readable-stream@~1.0.17, readable-stream@~1.0.2, readable-stream@~1.0.26, readable-stream@~1.0.31:
"readable-stream@>=1.0.33-1 <1.1.0-0", readable-stream@^1.0.33-1, readable-stream@~1.0.0, readable-stream@~1.0.17, readable-stream@~1.0.2, readable-stream@~1.0.26, readable-stream@~1.0.31:
version "1.0.34"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
dependencies: