Compare commits

...

86 Commits

Author SHA1 Message Date
Peter Bacon Darwin 22b669407c docs(CHANGELOG.md): add release notes for v1.5.8 2016-07-22 16:01:46 +01:00
Georgios Kalpakas 99a063f9cb docs(CHANGELOG.md): add release notes for v1.2.30 2016-07-22 15:45:08 +01:00
Georgios Kalpakas 27db9f018e test($anchorScroll): add e2e tests
Fixes #9535
Closes #9583

Closes #14932
2016-07-20 11:45:09 +03:00
Brad 2ed2399d49 docs($httpParamSerializer): fix typo (remove superfluous quote)
Closes #14934
2016-07-20 10:48:32 +03:00
Georgios Kalpakas 9d2de344f5 refactor($http): clean up code
Closes #14921
2016-07-19 23:25:11 +03:00
Georgios Kalpakas 9c0659ca27 refactor($http): move functions together and order alphabetically
Closes #14921
2016-07-19 23:10:39 +03:00
Georgios Kalpakas 3b9963c54a refactor(test/e2e): make file/directory names consistent 2016-07-19 21:46:34 +03:00
Georgios Kalpakas 26db937377 test($resource): add some tests wrt handling response errors
The test currently fail on master, but should pass on 1.5.x.
A subsequent commit will fix the regressions on master.

(This commit should be backportable to 1.5.x.)

Related to #14837.
2016-07-18 14:55:55 +01:00
Georgios Kalpakas 1d0e582b3f chore(protractor): use directConnect locally to speed up tests
Closes #14919
2016-07-18 10:03:41 +01:00
Georgios Kalpakas a6b58019e1 refactor(test/e2e): remove explicit call to waitForAngular()
Protractor automatically calls `waitForAngular()`, before every WebDriver action.

Closes #14920
2016-07-18 10:03:40 +01:00
Georgios Kalpakas 38fd896b74 refactor(test/e2e): restore consistency across tests (just because) 2016-07-18 10:03:40 +01:00
Georgios Kalpakas f96d553822 docs(guide): fix links to tutorial steps 2016-07-15 20:07:10 +03:00
John (J5) Palmieri f4149b70bc doc(guide/Animations): point to $animate.pin() to enable animation for elements outside an Angular app
Add a section inside of the ngAnimate documentation to point to $animate.pin().
It was not clear in the documentation why animations were being disabled inside of
modals and popup menus. This documentation explains in what situations elements may
be dynamically placed outside of the application DOM and points to the API documentation
on how to enable animations for these types of elements.

Related #14907
Closes #14918
2016-07-15 18:07:37 +02:00
Martin Staffa 8a8c94c3d5 docs(ngSelected): note that it doesn't interact with ngModel
Related #14916
Related #14876
2016-07-15 17:49:22 +02:00
Michał Gołębiowski d9de526059 test(jQuery): Run tests with jQuery 3
(cherry-picked from 859b1e300c)

Closes #14874
2016-07-13 10:13:58 +02:00
Georgios Kalpakas 0318761f7a refactor($animate): avoid unnecessary lookup
Closes #14902
2016-07-12 22:32:38 +03:00
Ronan Connolly 74f7b57823 docs(guide/unit-testing): add missing heading
Closes #14901
2016-07-12 18:31:30 +03:00
Steve Berube 995ccfc9f6 docs(guide/animations): use the arguments of enabled() in correct order
Closes #14900
2016-07-12 17:52:05 +03:00
Georgios Kalpakas 72c244564a chore(Jenkins): fix e2e tests on Jenkins
Follow-up to c72e13f and a82a8a5.

Closes #14889
2016-07-11 13:37:43 +03:00
Georgios Kalpakas 4bfe7e8663 docs(guide/module): remove newline in the middle of sentence 2016-07-10 14:13:33 +03:00
Oliver Salzburg 63fe144397 docs($http): fix default $http cache name
The name of the cache is the string "$http", not the symbol itself.

Closes #14885
2016-07-09 10:23:30 +03:00
Martin Staffa 0f551e6109 chore(protractor): use jasmine2 as framework 2016-07-08 11:30:10 +02:00
Martin Staffa 541f4ba21f chore(package.json): update jasmine-reporters to 2.2.0 2016-07-08 11:30:08 +02:00
Georgios Kalpakas 6e7d04ddb2 docs(guide/component): improve tests
Fixes #14739
2016-07-06 13:40:47 +03:00
Jack 06fb391687 docs(guide/directive): remove confusing best practice
Prior to this point, the docs do not mention returning a postLink function, and all the examples use
the definition object form. So, this message is confusing to new readers who may misinterpret
"returning a function" as returning the factory function mentioned in the previous section.

Since this page is only a "gentle introduction" to directives, and using the definition object is a
best practice, it is best to just remove the message altogether.

Closes #14871
2016-07-06 12:07:15 +03:00
Georgios Kalpakas 203dd4d1e3 docs(tutorial): fix typo in images (phoneList --> phoneDetail)
Fixes #14858

Closes #14868
2016-07-05 21:52:37 +03:00
Georgios Kalpakas c991f93817 docs(guide/interpolation): add known issue (do not change the content of interpolated strings)
Closes #12813

Closes #14825
2016-07-05 00:11:26 +03:00
Peter Bacon Darwin 52ddc13bee chore(package.json): update dgeni-packages dependency to 0.14.0 2016-07-04 21:31:47 +01:00
Igor Zhukov 1645924d49 fix(copy): fix handling of typed subarrays
Previously, it would return a copy of the whole original typed array, not its slice.
Now, the `byteOffset` and `length` are also preserved.

Fixes #14842

Closes #14845
2016-07-04 23:24:16 +03:00
Georgios Kalpakas 6bee655679 docs(copy): mention ignoring non-enumerable properties
This also improves the example a bit:
- better code formatting
- initialization of `form` to an empty object
- avoid using `email`, which doesn't get coppied when invalid (and might confuse users)

Fixes #14853
2016-07-04 10:16:08 +03:00
David Rodenas Pico acd455181d perf($compile): wrap try/catch of collect comment directives into a function to avoid V8 deopt
Closes #14848
2016-07-04 08:56:52 +03:00
jfisher446 a89b6e27a9 docs(tutorial/step_04): fix syntactic error (add missing 'a')
Closes #14860
2016-07-04 08:43:16 +03:00
mmuppa 46d559dc8d docs(misc/Develop): using https:// works more widely than git@
Closes #14838
2016-07-01 11:18:33 +01:00
Peter Bacon Darwin 26212fb204 docs(guide): tweaks to the index and external resources guides
Closes #14843
2016-07-01 11:18:33 +01:00
Martin Staffa 244cfc7cd5 docs(guide): create new page for external resources
The official and external resources  have been split into two different guide sections. Official stays at the index, external gets its own page.

The external resources have also been reorganized and updated, but
I haven't checked if all material is still relevant.
2016-07-01 11:18:33 +01:00
Jeff Andrews c6074dc34c fix(ngMocks): allow ErrorAddingDeclarationLocationStack to be recognized as an Error
Change `ErrorAddingDeclarationLocationStack`'s prototype so test frameworks (such as Jasmine 2.x)
are able to recognize it as `Error`.

Fixes #13821

Closes #14344
2016-06-29 13:44:45 +03:00
atenhar 4930ef2bfe docs(tutorial/step_03): fix grammar and typos
Closes #14834
2016-06-28 11:08:41 +03:00
Georgios Kalpakas 1778d347cd fix($jsonpCallbacks): do not overwrite callbacks added by other apps
Closes #14824
2016-06-27 15:18:40 +03:00
Georgios Kalpakas 4724d56c93 fix($injector): fix class detection RegExp
Mentioned in https://github.com/angular/angular.js/pull/14531#discussion_r61410683.

Closes #14533
2016-06-27 13:35:50 +03:00
Georgios Kalpakas 70fcffc375 test($resource): make test for function as param more explicit
Related to aa8d783.

Closes #14820
2016-06-27 11:54:29 +03:00
Georgios Kalpakas 181e44019e fix($animate): do not get affected by custom, enumerable properties on Object.prototype
Fixes #14804

Closes #14830
2016-06-27 11:50:31 +03:00
Liran Tal 5ce859bcdd docs(guide/filter): add link to list of built-in filters
Closes #14828
2016-06-26 20:28:40 +03:00
Deplay fb994e4d90 refactor(jqLite): remove unused code in jqLiteBuildFragment()
Closes #14822
2016-06-25 22:09:43 +03:00
GARCIA Guillaume 55f26fc246 docs(*): correct bower version separator (@ --> #)
Closes #14819
2016-06-25 21:56:58 +03:00
Martin Staffa 1161002857 docs(ngReadonly): note input type restrictions
Closes #14816
2016-06-23 12:02:01 +02:00
Martin Staffa 7ccfe92bed fix(ngAnimate): allow removal of class that is scheduled to be added with requestAnimationFrame
Also affects the reverse case, adding a class that is scheduled to be removed with rAF.

The following case can happen when ngClass updates an element's classes in very quick order in the following way:

- First animation adds class "a"
- A digest passes, but "a" is not yet added to the element
- Second animation adds class "b"
- No digest passes, and "a" is still not added to the element,
  because requestAnimationFrame hasn't been flushed yet
- Third animation removes class "a"
- the third animation gets merged into the second animation

Before this change:

- Because the element doesn't have class "a" yet, ngAnimate
resolves that it cannot remove class "a". However,
the first animation is still running, and finally adds "a"

After this change:

- ngAnimate reacts to the temporary class "add-a", which indicates
that "a" is about to be added and decides that "a" can be removed
after all.

This is a very rare case where setting the element's class
is not fast enough, and subsequent animations operate on incorrect assumptions.

"In the wild", this is caused by rapidly updating ngClass,
which uses inidvidual addClass and removeClass calls when both operations happen in a single digest.

Fixes #14582
PR  (#14760)
2016-06-23 12:01:57 +02:00
Chirayu Krishnappa a126fcfee3 feat($resource): pass the resource to a dynamic param functions
Closes #4899
2016-06-22 12:22:16 +01:00
Peter Bacon Darwin f22af2b874 docs($http): fix a dangling link 2016-06-22 11:19:22 +01:00
Peter Bacon Darwin 2ca4170903 docs($compile): reorganize the life-cycle hooks docs
Closes #14811
2016-06-22 11:19:21 +01:00
Peter Bacon Darwin fd57df1235 docs($compile): add additional runnable examples for the $doCheck hook
Closes #14811
2016-06-22 11:19:21 +01:00
Peter Bacon Darwin 3010ed4ee5 fix($compile): ensure $doCheck hooks can be defined in the controller constructor
Closes #14811
2016-06-22 11:19:21 +01:00
Zach Bjornson de59ca7107 feat($compile): backport $doCheck
Backuport ngDoCheck from Angular 2.

Closes #14656
2016-06-22 11:19:21 +01:00
Peter Bacon Darwin fa3214cfb8 refact($http): use the $jsonpCallbacks service
Use the built-in service to handling callbacks to `$http.jsonp` requests.

Closes #3073
Closes #14795
2016-06-21 19:10:13 +01:00
Peter Bacon Darwin a8cacfe938 feat($jsonpCallbacks): new service to abstract how JSONP callbacks are handled
You can now override this service if you have specific requirements about
the behaviour and formatting of the JSON_CALLBACK that is sent to the server
for `$http.jsonp` requests.

Closes #14795
2016-06-21 19:08:28 +01:00
Lucas Galfasó 1a387ba5dd fix($timeout): make $flush handle new $timeouts added in $timeout callbacks
If a $timeout handler calls $timeout itself, this new $timeout should be
added to the mock deferred queue with a "now" time based on when the original
handler was triggered.

Previously it was being added with a now time of when the `$timeout.flush`
method was originally called.

Closes #5420
Closes #14686
2016-06-20 10:01:19 -07:00
Georgios Kalpakas 98e4a220fe fix(modules): allow modules to be loaded in any order when using angular-loader
Some modules used to assume that the angular helpers would always be available when their script was
executed. This could be a problem when using `angular-loader` and the module file happened to get
loaded before the core `angular.js` file.
This commit fixes the issue by delaying the access to angular helpers, until they are guaranteed to
be available.

Affected modules:
- `ngAnimate`
- `ngMessageFormat`
- `ngMessages`
- `ngRoute`
- `ngSanitize`

Fixes #9140

Closes #14794
2016-06-18 00:20:56 +03:00
Georgios Kalpakas 9001ae2822 docs(misc/downloading): mention ngMessageFormat 2016-06-18 00:20:54 +03:00
Peter Bacon Darwin c405f88bbc fix(ngTransclude): ensure that fallback content is compiled and linked correctly
Closes #14787
2016-06-17 10:59:51 +01:00
Daniel Herman 159a68ec7b fix(ngTransclude): only compile fallback content if necessary
If the instance of the directive does provide transcluded content, then the fallback
content should not be compiled and linked as it will never be used.

If the instance of the directive does not provide transcluded content, then the
transcluded scope that was created for this non-existent content is never used,
so it should be destroy in order to clean up unwanted memory use and digests.

Fixes #14768
Fixes #14765
Closes #14775
2016-06-17 10:52:57 +01:00
Yihang Ho b9a56d588f feat($q): implement $q.race
Implement $q.race. $q.race takes in an array or hash of promises and
returns a promise that resolves or rejects as soon as one of those promises
resolves or rejects, with the value or reason from that promise.

Closes #12929
Closes #14757
2016-06-17 10:21:31 +01:00
Abdulkader f797f83cd6 feat($swipe): add pointer support
Add pointer events to $swipe to support IE11 on touch devices

Closes #14061
Closes #14791
2016-06-17 09:52:43 +01:00
Georgios Kalpakas 6df970eb3c docs($resource): clarify how a @-prefix param is used 2016-06-17 00:45:34 +03:00
Georgios Kalpakas f88b4be92f docs(angular.mock.dump): remove incorrect statement
Fixes #14790
2016-06-17 00:26:43 +03:00
Roman Rodych a7bcc01581 refactor(input[number]): remove redandant second argument to parseFloat()
Closes #14793
2016-06-16 22:19:08 +03:00
Martin Staffa 40ed8094d1 chore(changelog.js): don't add empty breaking change section 2016-06-16 14:38:53 +02:00
Martin Staffa 1a722f7b19 docs($compile): add link to transcludeFn description
Closes #14764
2016-06-16 14:16:59 +02:00
Martin Staffa 4991207c7c chore(version-info): make online build compatible with Windows
Since we cannot write to dev/null on Windows, don't write to a file at all, but simply use stdout and extract the output from --write-out from the process response. Credit to @petebacondarwin for the idea.

The commit also avoids a premature error when no cdnVersion could be found online, and improves the log wrt the origin of the versions.

PR (#14780)
2016-06-16 14:16:59 +02:00
Georgios Kalpakas 33514ec384 feat(filterFilter): allow overwriting the special $ property name
Previously, the special property name that would match against any
property was hard-coded to `$`.
With this commit, the user can specify an arbitrary property name,
by passing a 4th argument to `filterFilter()`. E.g.:

```js
var items = [{foo: 'bar'}, {baz: 'qux'}];
var expr  =  {'%': 'bar'};

console.log(filterFilter(items, expr, null, '%'));   // [{foo: 'bar'}]
```

Fixes #13313
PR (#13356)
2016-06-16 14:15:55 +02:00
Georgios Kalpakas 28bd20ed30 docs($interpolate): fix heading 2016-06-16 14:15:55 +02:00
Martin Staffa c3bfd7f59d fix(ngOptions): don't duplicate groups with falsy values
Previously, ngOptions would fail to remove optgroups with falsy values when the options were changed / removed.

Related #14781 
PR (#14784)
2016-06-16 14:15:54 +02:00
Georgios Kalpakas 553cccdb85 docs(CHANGELOG.md): add release notes for v1.5.7 2016-06-16 14:15:06 +02:00
Martin Staffa c7dd2c24f4 docs(guide/filters): clarify when filters are executed
Closes #14590
PR  (#14758)
2016-06-16 14:13:01 +02:00
Peter Bacon Darwin fbf5aebda2 chore(version-info): add offline-build support
Previously it was slow and problematic to run builds if you were not connected
to the internet because the build scripts make requests to github and the
Google CDN to find out version information.

You can now disable these requests by setting the NG1_BUILD_NO_REMOTE_VERSION_REQUESTS
environment variable to a non-empty value

Closes #14769
2016-06-15 10:32:31 +01:00
Peter Bacon Darwin ab35c16ccf chore(app.scenario): test docs app against production libraries
We have had issues where the docs application has gone down because
our build included invalid URLs to the production angular libraries.

This change runs a subset of the docs e2e tests directly against these
production libraries to ensure that they are accessible. If not then these
tests will fail and the CI build will abort, preventing the docs app from
being updated.

Closes #14769
2016-06-15 10:32:18 +01:00
Peter Bacon Darwin 37b6a71678 chore(Gruntfile): kill the build if the CDN version was not found
Closes #14769
2016-06-15 10:32:07 +01:00
Martin Staffa e79ebff223 docs(changelog.md): add 1.5.0-beta.0 entry
This was a preparation release for the 1.5.x branch,
which only contains one fix over the previous 1.4.6
release.
2016-06-15 11:22:03 +02:00
Wesley Cho 36b38e1355 docs($q): add note about optional arguments for promise.then()
- Add note about optional arguments in the promise.then api

Closes #8562
PR (#14754)
2016-06-15 11:21:58 +02:00
Wesley Cho 3168d95793 docs(guide): add notes about using ngAttr for IE
- Add note about using the value attribute for the progress tag with IE in the IE compat / interpolation guide
- Add note about placeholder attribute in textarea to the ie compat guide

Closes #7218
PR (#14753)
2016-06-15 11:21:39 +02:00
Geoffrey Bauduin 538c63f057 docs($exceptionHandler): add missing end bracket in example
PR  (#14756)
2016-06-15 11:21:21 +02:00
Wesley Cho a9a1461351 docs(ngOpen): add note about browser compatibility
- Add note about browser compatibility and recommend ng-show/ng-hide

Closes ##7337
PR (#14752)
2016-06-15 11:21:11 +02:00
Peter Bacon Darwin 29190f9318 revert: chore(version-info): disable remote requests when offline
This reverts commit aa38ded59d, which
caused the docs to build incorrectly causing the docs website to crash.
2016-06-14 20:44:12 +01:00
Peter Bacon Darwin ca7d2b3b3f docs(ngMessageFormat): fix e2e test in example 2016-06-14 20:41:23 +01:00
Peter Bacon Darwin 81676f8323 style(ngMessageFormat): fix jscs whitespace error 2016-06-14 20:41:23 +01:00
Martin Staffa 6f9d07d9c7 chore(docsApp): don't display module components if none exist
Some modules (ngMessageFormat, ngParseExt) don't have their own api surface
because they integrate with core services ($interpolate and $parse respectively).

Closes #14770
2016-06-14 20:41:23 +01:00
Martin Staffa 5201b8146a docs(ngMessageFormat): move docs for $$messageFormat to module namespace.
This is a private service that should not be documented.
The docs have been moved to the ngMessageFormat module namespace.

Closes #14770
2016-06-14 20:41:23 +01:00
Martin Staffa 6aa83d04b7 docs(ngMessageFormat): add more examples
Closes #14770
2016-06-14 20:41:23 +01:00
109 changed files with 3461 additions and 2694 deletions
+188 -1
View File
@@ -1,3 +1,178 @@
<a name="1.5.8"></a>
# 1.5.8 arbitrary-fallbacks (2016-07-22)
## Bug Fixes
- **$animate:** do not get affected by custom, enumerable properties on `Object.prototype`
([181e4401](https://github.com/angular/angular.js/commit/181e44019e850e5253378e29415cddf8d768bbef),
[#14804](https://github.com/angular/angular.js/issues/14804), [#14830](https://github.com/angular/angular.js/issues/14830))
- **$compile:** ensure `$doCheck` hooks can be defined in the controller constructor
([3010ed4e](https://github.com/angular/angular.js/commit/3010ed4ee5c2c18b9848b5664639971b9fdc8919),
[#14811](https://github.com/angular/angular.js/issues/14811))
- **$injector:** fix class detection RegExp
([4724d56c](https://github.com/angular/angular.js/commit/4724d56c939d02675433e7fe554608dff97ecf81),
[#14533](https://github.com/angular/angular.js/issues/14533))
- **$jsonpCallbacks:** do not overwrite callbacks added by other apps
([1778d347](https://github.com/angular/angular.js/commit/1778d347cd3c91335e3840eaa49f1e387db8602f),
[#14824](https://github.com/angular/angular.js/issues/14824))
- **$timeout:** make $flush handle new $timeouts added in $timeout callbacks
([1a387ba5](https://github.com/angular/angular.js/commit/1a387ba5dd1a8a831486fce23f6795bd1eef3d8b),
[#5420](https://github.com/angular/angular.js/issues/5420), [#14686](https://github.com/angular/angular.js/issues/14686))
- **copy:** fix handling of typed subarrays
([1645924d](https://github.com/angular/angular.js/commit/1645924d49a7905ce55cced4c4276654970bb226),
[#14842](https://github.com/angular/angular.js/issues/14842), [#14845](https://github.com/angular/angular.js/issues/14845))
- **modules:** allow modules to be loaded in any order when using `angular-loader`
([98e4a220](https://github.com/angular/angular.js/commit/98e4a220fe8301cec35498ae592adc0266f12437),
[#9140](https://github.com/angular/angular.js/issues/9140), [#14794](https://github.com/angular/angular.js/issues/14794))
- **ngAnimate:** allow removal of class that is scheduled to be added with requestAnimationFrame
([7ccfe92b](https://github.com/angular/angular.js/commit/7ccfe92bed7361832f1b8d25b1a8411eb24d3fb5),
[#14582](https://github.com/angular/angular.js/issues/14582))
- **ngMocks:** allow `ErrorAddingDeclarationLocationStack` to be recognized as an `Error`
([c6074dc3](https://github.com/angular/angular.js/commit/c6074dc34c31a07269bf7f628b971ef6dc805f17),
[#13821](https://github.com/angular/angular.js/issues/13821), [#14344](https://github.com/angular/angular.js/issues/14344))
- **ngOptions:** don't duplicate groups with falsy values
([c3bfd7f5](https://github.com/angular/angular.js/commit/c3bfd7f59d0ecbf4ba3253fb407e683c7bb0766c))
- **ngTransclude:**
- ensure that fallback content is compiled and linked correctly
([c405f88b](https://github.com/angular/angular.js/commit/c405f88bbc743f41591f6f3cfc022eea3c6c34ad),
[#14787](https://github.com/angular/angular.js/issues/14787))
- only compile fallback content if necessary
([159a68ec](https://github.com/angular/angular.js/commit/159a68ec7ba77e9128b0d0516b813ed3d223b6e3),
[#14768](https://github.com/angular/angular.js/issues/14768), [#14765](https://github.com/angular/angular.js/issues/14765), [#14775](https://github.com/angular/angular.js/issues/14775))
## Features
- **$compile:** backport $doCheck
([de59ca71](https://github.com/angular/angular.js/commit/de59ca71072eac95ee68de308f92bc5f921dd07b),
[#14656](https://github.com/angular/angular.js/issues/14656))
- **$jsonpCallbacks:** new service to abstract how JSONP callbacks are handled
([a8cacfe9](https://github.com/angular/angular.js/commit/a8cacfe938287c54ce7099125cb735ad53f4c7c2),
[#14795](https://github.com/angular/angular.js/issues/14795))
- **$q:** implement $q.race
([b9a56d58](https://github.com/angular/angular.js/commit/b9a56d588f8b597b1dff30d8e184b7c37d94cdcf),
[#12929](https://github.com/angular/angular.js/issues/12929), [#14757](https://github.com/angular/angular.js/issues/14757))
- **$resource:** pass the resource to a dynamic param functions
([a126fcfe](https://github.com/angular/angular.js/commit/a126fcfee3bd8b02869bd2542c73e1eb21afe927),
[#4899](https://github.com/angular/angular.js/issues/4899))
- **$swipe:** add pointer support
([f797f83c](https://github.com/angular/angular.js/commit/f797f83cd66f1fd11b3c9399e7894217ffa06c38),
[#14061](https://github.com/angular/angular.js/issues/14061), [#14791](https://github.com/angular/angular.js/issues/14791))
- **filterFilter:** allow overwriting the special `$` property name
([33514ec3](https://github.com/angular/angular.js/commit/33514ec384d676d84b2a445bc15bae38c8c3ac8d),
[#13313](https://github.com/angular/angular.js/issues/13313))
## Performance Improvements
- **$compile:** wrap try/catch of collect comment directives into a function to avoid V8 deopt
([acd45518](https://github.com/angular/angular.js/commit/acd455181de1cfa6b34d75f8d71a6c0b6995a777),
[#14848](https://github.com/angular/angular.js/issues/14848))
<a name="1.2.30"></a>
# 1.2.30 patronal-resurrection (2016-07-21)
_**Note:** This release contains some security fixes that required breaking changes. Since the
legacy 1.2.x branch is the only version branch that supports IE8, it was necessary to introduce a
couple of low-impact breaking changes in a patch release - something we generally avoid - in order
to make the fixes available to people that still need IE8 support._
## Bug Fixes
- **$compile:**
- secure `link[href]` as a `RESOURCE_URL`s in `$sce`
([f35f334b](https://github.com/angular/angular.js/commit/f35f334bd3197585bdf034f4b6d9ffa3122dac62),
[#14687](https://github.com/angular/angular.js/issues/14687))
- properly sanitize `xlink:href` attribute interoplation
([f2fa1ed8](https://github.com/angular/angular.js/commit/f2fa1ed83d18d4e79a36f8c0db1c2524d762e513),
[2687c261](https://github.com/angular/angular.js/commit/2687c26140585d9e3716f9f559390f5d8d598fdf))
- **ngSanitize:** blacklist the attribute `usemap` as it can be used as a security exploit
([ac0d5286](https://github.com/angular/angular.js/commit/ac0d5286b8931633d774080d6396fb4825d8be33),
[#14903](https://github.com/angular/angular.js/issues/14903))
- **ngAnimate:** do not use event.timeStamp anymore for time tracking
([8d83b563](https://github.com/angular/angular.js/commit/8d83b5633471c847d58f337426fe069797dd49d9),
[#13494](https://github.com/angular/angular.js/issues/13494), [#13495](https://github.com/angular/angular.js/issues/13495))
## Breaking Changes
- **$compile:** due to [f35f334b](https://github.com/angular/angular.js/commit/f35f334bd3197585bdf034f4b6d9ffa3122dac62),
`link[href]` attributes are now protected via `$sce`, which prevents interpolated values that fail
the `RESOURCE_URL` context tests from being used in interpolation. For example if the application is
running at `https://mydomain.org/` then the following will fail:
```html
<link rel="stylesheet" href="{{ 'https://otherdomain.org/unsafe.css' }}" />
```
By default, `RESOURCE_URL` safe URLs are only allowed from the same domain and protocol as the
application document. To use URLs from other domains and/or protocols, you may either whitelist them
using `$sceDelegateProvider.resourceUrlWhitelist(...)` or wrap them into a trusted value by calling
`$sce.trustAsResourceUrl(url)`.
- **ngSanitize:** due to [234053fc](https://github.com/angular/angular.js/commit/234053fc9ad90e0d05be7e8359c6af66be94c094),
The `$sanitize` service will now remove instances of the `usemap` attribute from any elements passed
to it.
This attribute is used to reference another element by `name` or `id`. Since the `name` and `id`
attributes are already blacklisted, a sanitized `usemap` attribute could only reference unsanitized
content, which is a security risk.
<a name="1.5.7"></a>
# 1.5.7 hexagonal-circumvolution (2016-06-15)
## Bug Fixes
- **$compile:**
- don't add merged attributes twice to $attrs
([ebbaa4ac](https://github.com/angular/angular.js/commit/ebbaa4ac5e3559df9267203438a0bb18c2d3b7d8),
[#8159](https://github.com/angular/angular.js/issues/8159), [#14737](https://github.com/angular/angular.js/issues/14737))
- cope with `$onChanges` hooks throwing
([3749c858](https://github.com/angular/angular.js/commit/3749c85829406ca57cc5729e80696c7f34134068),
[#14444](https://github.com/angular/angular.js/issues/14444), [#14463](https://github.com/angular/angular.js/issues/14463))
- **$parse:** allow arguments to contain filter chains
([cc6dcb4b](https://github.com/angular/angular.js/commit/cc6dcb4bc28aadff4f62d76d6451b0f80b928e69),
[#4175](https://github.com/angular/angular.js/issues/4175), [#4168](https://github.com/angular/angular.js/issues/4168), [#14720](https://github.com/angular/angular.js/issues/14720))
- **$routeProvider:** do not deep-copy route definition objects
([6d0dcca6](https://github.com/angular/angular.js/commit/6d0dcca6f18a353a12c356dc96e05475d351b795),
[#14478](https://github.com/angular/angular.js/issues/14478), [#14699](https://github.com/angular/angular.js/issues/14699), [#14750](https://github.com/angular/angular.js/issues/14750))
- **input[email]:** improve email address validation
([f423dc6b](https://github.com/angular/angular.js/commit/f423dc6b51a9b4a09728a287a2cabda7d45f192e),
[#14719](https://github.com/angular/angular.js/issues/14719))
- **ngMessages:** create new scope for ngMessage, clean it up correctly
([56f33af8](https://github.com/angular/angular.js/commit/56f33af89045e2ec18d144d9d1ef73affbe51959),
[#14307](https://github.com/angular/angular.js/issues/14307))
- **ngMessagesInclude:** don't break on empty (or whitespace-only) templates
([01b8772c](https://github.com/angular/angular.js/commit/01b8772cca55916376355a2ae58d3ab7832a4bc2),
[#12941](https://github.com/angular/angular.js/issues/12941), [#14726](https://github.com/angular/angular.js/issues/14726))
- **ngMock#$controller:** properly assign bindings to all types of controllers (e.g. class-based)
([db58801a](https://github.com/angular/angular.js/commit/db58801a55c91df755414387dc00fee5902bb5f3),
[#14437](https://github.com/angular/angular.js/issues/14437), [#14439](https://github.com/angular/angular.js/issues/14439))
- **ngMockE2E:** allow $httpBackend.passThrough() to work when ngMock is loaded
([6685043a](https://github.com/angular/angular.js/commit/6685043ad40acc50d7088f87e2a71f76d2265306),
[#1434](https://github.com/angular/angular.js/issues/1434), [#13124](https://github.com/angular/angular.js/issues/13124))
- **ngSanitize:** call attribute setter in linky for all links
([c4fad6ad](https://github.com/angular/angular.js/commit/c4fad6ad617af025984ca401054f7b402aa28f1d),
[#14707](https://github.com/angular/angular.js/issues/14707))
## Features
- **limitTo:** add support for array-like objects
([b03957f0](https://github.com/angular/angular.js/commit/b03957f0047fcfe996abc8efe97f9e2be2c2e13a),
[#14657](https://github.com/angular/angular.js/issues/14657), [#14694](https://github.com/angular/angular.js/issues/14694))
- **orderBy:** add support for custom comparators
([2e33d170](https://github.com/angular/angular.js/commit/2e33d170be7eb72f40fd57a8f66631583572fbf2),
[#13238](https://github.com/angular/angular.js/issues/13238), [#14455](https://github.com/angular/angular.js/issues/14455), [#5123](https://github.com/angular/angular.js/issues/5123), [#8112](https://github.com/angular/angular.js/issues/8112), [#10368](https://github.com/angular/angular.js/issues/10368), [#14468](https://github.com/angular/angular.js/issues/14468))
<a name="1.5.6"></a>
# 1.5.6 arrow-stringification (2016-05-27)
@@ -512,7 +687,8 @@ changes section for more information
- **ngSanitize:** due to [234053fc](https://github.com/angular/angular.js/commit/234053fc9ad90e0d05be7e8359c6af66be94c094),
The `$sanitize` service will now remove instances of the `usemap` attribute from any elements passed to it.
The `$sanitize` service will now remove instances of the `usemap` attribute from any elements passed
to it.
This attribute is used to reference another element by `name` or `id`. Since the `name` and `id`
attributes are already blacklisted, a sanitized `usemap` attribute could only reference unsanitized
@@ -1443,6 +1619,17 @@ requirement more strict and alerts the developer explicitly.
[#9936](https://github.com/angular/angular.js/issues/9936))
<a name="1.5.0-beta.0"></a>
# 1.5.0-beta.0 intialization-processation (2015-09-17)
## Bug Fixes
- **jqLite:**: properly handle dash-delimited node names in `jqLiteBuildFragment`
([cdd1227a3](https://github.com/angular/angular.js/commit/cdd1227a308edd34d31b67f338083b6e0c4c0db9),
[#10617](https://github.com/angular/angular.js/issues/10617))
<a name="1.4.6"></a>
# 1.4.6 multiplicative-elevation (2015-09-17)
+8 -1
View File
@@ -17,6 +17,10 @@ module.exports = function(grunt) {
NG_VERSION.cdn = versionInfo.cdnVersion;
var dist = 'angular-'+ NG_VERSION.full;
if (versionInfo.cdnVersion == null) {
throw new Error('Unable to read CDN version, are you offline or has the CDN not been properly pushed?');
}
//config
grunt.initConfig({
NG_VERSION: NG_VERSION,
@@ -79,6 +83,7 @@ module.exports = function(grunt) {
tests: {
jqlite: 'karma-jqlite.conf.js',
jquery: 'karma-jquery.conf.js',
'jquery-2.2': 'karma-jquery-2.2.conf.js',
'jquery-2.1': 'karma-jquery-2.1.conf.js',
docs: 'karma-docs.conf.js',
modules: 'karma-modules.conf.js'
@@ -88,6 +93,7 @@ module.exports = function(grunt) {
autotest: {
jqlite: 'karma-jqlite.conf.js',
jquery: 'karma-jquery.conf.js',
'jquery-2.2': 'karma-jquery-2.2.conf.js',
'jquery-2.1': 'karma-jquery-2.1.conf.js',
modules: 'karma-modules.conf.js',
docs: 'karma-docs.conf.js'
@@ -360,10 +366,11 @@ module.exports = function(grunt) {
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'jscs', 'package', 'test:unit', 'test:promises-aplus', 'tests:docs', 'test:protractor']);
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
grunt.registerTask('test:jquery', 'Run the jQuery (latest) unit tests with Karma', ['tests:jquery']);
grunt.registerTask('test:jquery-2.2', 'Run the jQuery 2.2 unit tests with Karma', ['tests:jquery-2.2']);
grunt.registerTask('test:jquery-2.1', 'Run the jQuery 2.1 unit tests with Karma', ['tests:jquery-2.1']);
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['build', 'tests:modules']);
grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']);
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['test:jqlite', 'test:jquery', 'test:jquery-2.1', 'test:modules']);
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['test:jqlite', 'test:jquery', 'test:jquery-2.2', 'test:jquery-2.1', 'test:modules']);
grunt.registerTask('test:protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'protractor:normal']);
grunt.registerTask('test:travis-protractor', 'Run the end to end tests with Protractor for Travis CI builds', ['connect:testserver', 'protractor:travis']);
grunt.registerTask('test:ci-protractor', 'Run the end to end tests with Protractor for Jenkins CI builds', ['webdriver', 'connect:testserver', 'protractor:jenkins']);
+12 -9
View File
@@ -28,6 +28,7 @@ var angularFiles = {
'src/ng/httpBackend.js',
'src/ng/interpolate.js',
'src/ng/interval.js',
'src/ng/jsonpCallbacks.js',
'src/ng/locale.js',
'src/ng/location.js',
'src/ng/log.js',
@@ -236,15 +237,6 @@ var angularFiles = {
'@angularTest'
],
'karmaJqueryOld': [
'bower_components/jquery-2.1/dist/jquery.js',
'test/jquery_alias.js',
'@angularSrc',
'@angularSrcModules',
'@angularScenario',
'@angularTest'
],
'karmaJqueryExclude': [
'src/angular-bootstrap.js',
'src/ngScenario/angular-bootstrap.js',
@@ -253,6 +245,17 @@ var angularFiles = {
]
};
['2.1', '2.2'].forEach(function (jQueryVersion) {
angularFiles['karmaJquery' + jQueryVersion] = []
.concat(angularFiles.karmaJquery)
.map(function (path) {
if (path.startsWith('bower_components/jquery')) {
return path.replace(/^bower_components\/jquery/, 'bower_components/jquery-' + jQueryVersion);
}
return path;
});
});
angularFiles['angularSrcModules'] = [].concat(
angularFiles['angularModules']['ngAnimate'],
angularFiles['angularModules']['ngMessageFormat'],
+2 -1
View File
@@ -2,7 +2,8 @@
"name": "AngularJS",
"license": "MIT",
"devDependencies": {
"jquery": "2.2.3",
"jquery": "3.1.0",
"jquery-2.2": "jquery#2.2.4",
"jquery-2.1": "jquery#2.1.4",
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip"
-2
View File
@@ -148,8 +148,6 @@ var writeChangelog = function(stream, commits, version) {
breaks: {}
};
sections.breaks[EMPTY_COMPONENT] = [];
commits.forEach(function(commit) {
var section = sections[commit.type];
var component = commit.component || EMPTY_COMPONENT;
+6 -6
View File
@@ -39,7 +39,7 @@ describe('docs.angularjs.org', function () {
it('should change the page content when clicking a link to a service', function () {
browser.get('build/docs/index.html');
browser.get('build/docs/index-production.html');
var ngBindLink = element(by.css('.definition-table td a[href="api/ng/directive/ngClick"]'));
ngBindLink.click();
@@ -51,33 +51,33 @@ describe('docs.angularjs.org', function () {
it('should be resilient to trailing slashes', function() {
browser.get('build/docs/index.html#!/api/ng/function/angular.noop/');
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/');
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('angular.noop');
});
it('should be resilient to trailing "index"', function() {
browser.get('build/docs/index.html#!/api/ng/function/angular.noop/index');
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/index');
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('angular.noop');
});
it('should be resilient to trailing "index/"', function() {
browser.get('build/docs/index.html#!/api/ng/function/angular.noop/index/');
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/index/');
var pageBody = element(by.css('h1'));
expect(pageBody.getText()).toEqual('angular.noop');
});
it('should display formatted error messages on error doc pages', function() {
browser.get('build/docs/index.html#!error/ng/areq?p0=Missing&p1=not%20a%20function,%20got%20undefined');
browser.get('build/docs/index-production.html#!error/ng/areq?p0=Missing&p1=not%20a%20function,%20got%20undefined');
expect(element(by.css('.minerr-errmsg')).getText()).toEqual("Argument 'Missing' is not a function, got undefined");
});
it("should display an error if the page does not exist", function() {
browser.get('build/docs/index.html#!/api/does/not/exist');
browser.get('build/docs/index-production.html#!/api/does/not/exist');
expect(element(by.css('h1')).getText()).toBe('Oops!');
});
@@ -22,7 +22,7 @@
</li>
<li>
<a href="http://bower.io">Bower</a> e.g.
{% code %}bower install {$ doc.packageName $}@X.Y.Z{% endcode %}
{% code %}bower install {$ doc.packageName $}#X.Y.Z{% endcode %}
</li>
<li>
<a href="https://code.angularjs.org/">code.angularjs.org</a>
@@ -69,6 +69,7 @@
{% endif %}
{% if doc.componentGroups.length %}
<div class="component-breakdown">
<h2>Module Components</h2>
{% for componentGroup in doc.componentGroups %}
@@ -89,6 +90,7 @@
</div>
{% endfor %}
</div>
{% endif %}
{% if doc.usage %}
<h2>Usage</h2>
+2
View File
@@ -8,6 +8,8 @@ Welcome to the AngularJS API docs page. These pages contain the AngularJS refere
The documentation is organized into **{@link guide/module modules}** which contain various components of an AngularJS application.
These components are {@link guide/directive directives}, {@link guide/services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates templates}, global APIs, and testing mocks.
There is also a {@link guide/index guide} with articles on various topics, and a list of external resources.
<div class="alert alert-info">
**Angular Prefixes `$` and `$$`**:
+21 -3
View File
@@ -313,8 +313,8 @@ This function can be used to enable / disable animations in two different ways:
With a single `boolean` argument, it enables / disables animations globally: `$animate.enabled(false)`
disables all animations in your app.
When the second argument is a native DOM or jQuery element, the function enables / disables
animations on this element *and all its children*: `$animate.enabled(false, myElement)`. This is the
When the first argument is a native DOM or jqLite/jQuery element, the function enables / disables
animations on this element *and all its children*: `$animate.enabled(myElement, false)`. This is the
most flexible way to change the animation state. For example, even if you have used it to disable
animations on a parent element, you can still re-enable it for a child element. And compared to the
`classNameFilter`, you can change the animation status at runtime instead of during the config phase.
@@ -347,6 +347,7 @@ By setting `transition: 0s`, ngAnimate will ignore the existing transition style
animations will still execute, though). This can be used to prevent {@link guide/animations#preventing-collisions-with-existing-animations-and-third-party-libraries
issues with existing animations interfering with ngAnimate}.
## Preventing flicker before an animation starts
When nesting elements with structural animations such as `ngIf` into elements that have class-based
@@ -421,8 +422,25 @@ than any styles that are independent from ngAnimate.
You can also use one of the two other {@link guide/animations#how-to-selectively-enable-disable-and-skip-animations strategies to disable animations}.
### Enable animations for elements outside of the Angular application DOM tree: {@link ng.$animate#pin $animate.pin()}
Before animating, `ngAnimate` checks to see if the element being animated is inside the application DOM tree,
and if it is not, no animation is run. Usually, this is not a problem as most apps use the `ngApp`
attribute / bootstrap the app on the `html` or `body` element.
Problems arise when the application is bootstrapped on a different element, and animations are
attempted on elements that are outside the application tree, e.g. when libraries append popup and modal
elements as the last child in the body tag.
You can use {@link ng.$animate#pin `$animate.pin(elementToAnimate, parentHost)`} to specify that an
element belongs to your application. Simply call it before the element is added to the DOM / before
the animation starts, with the element you want to animate, and the element which should be its
assumed parent.
## More about animations
For a full breakdown of each method available on `$animate`, see the {@link ng.$animate API documentation}.
To see a complete demo, see the {@link tutorial/step_12 animation step within the AngularJS phonecat tutorial}.
To see a complete demo, see the {@link tutorial/step_14 animation step within the AngularJS phonecat tutorial}.
+36 -24
View File
@@ -236,6 +236,10 @@ it upwards to the heroList component, which updates the original data.
function HeroDetailController() {
var ctrl = this;
ctrl.delete = function() {
ctrl.onDelete({hero: ctrl.hero});
};
ctrl.update = function(prop, value) {
ctrl.onUpdate({hero: ctrl.hero, prop: prop, value: value});
};
@@ -303,7 +307,7 @@ it upwards to the heroList component, which updates the original data.
<div>
Name: {{$ctrl.hero.name}}<br>
Location: <editable-field field-value="$ctrl.hero.location" field-type="text" on-update="$ctrl.update('location', value)"></editable-field><br>
<button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
<button ng-click="$ctrl.delete()">Delete</button>
</div>
</file>
<file name="editableField.html">
@@ -443,44 +447,52 @@ angular.module('docsTabsExample', [])
# Unit-testing Component Controllers
The easiest way to unit-test a component controller is by using the {@link ngMock.$componentController $componentController}
that is included in {@link ngMock}. The advantage of this method is that you do not have
to create any DOM elements. The following example shows how to do this for the `heroDetail` component
from above.
The easiest way to unit-test a component controller is by using the
{@link ngMock.$componentController $componentController} that is included in {@link ngMock}. The
advantage of this method is that you do not have to create any DOM elements. The following example
shows how to do this for the `heroDetail` component from above.
The examples use the [Jasmine](http://jasmine.github.io/) testing framework.
**Controller Test:**
```js
describe('component: heroDetail', function() {
var component, scope, hero, $componentController;
var $componentController;
beforeEach(module('heroApp'));
beforeEach(inject(function($rootScope, _$componentController_) {
scope = $rootScope.$new();
beforeEach(inject(function(_$componentController_) {
$componentController = _$componentController_;
hero = {name: 'Wolverine'};
}));
it('should assign the name bindings to the hero object', function() {
it('should expose a `hero` object', function() {
// Here we are passing actual bindings to the component
component = $componentController('heroDetail',
null,
{hero: hero}
);
expect(component.hero.name).toBe('Wolverine');
var bindings = {hero: {name: 'Wolverine'}};
var ctrl = $componentController('heroDetail', null, bindings);
expect(ctrl.hero).toBeDefined();
expect(ctrl.hero.name).toBe('Wolverine');
});
it('should call the onDelete binding when a hero is deleted', function() {
var deleteSpy = jasmine.createSpy('deleteSpy');
component = $componentController('heroDetail',
null,
{hero: hero, onDelete: deleteSpy}
);
it('should call the `onDelete` binding, when deleting the hero', function() {
var onDeleteSpy = jasmine.createSpy('onDelete');
var bindings = {hero: {}, onDelete: onDeleteSpy};
var ctrl = $componentController('heroDetail', null, bindings);
component.onDelete({hero: component.hero});
expect(deleteSpy).toHaveBeenCalledWith({hero: component.hero});
ctrl.delete();
expect(onDeleteSpy).toHaveBeenCalledWith({hero: ctrl.hero});
});
it('should call the `onUpdate` binding, when updating a property', function() {
var onUpdateSpy = jasmine.createSpy('onUpdate');
var bindings = {hero: {}, onUpdate: onUpdateSpy};
var ctrl = $componentController('heroDetail', null, bindings);
ctrl.update('foo', 'bar');
expect(onUpdateSpy).toHaveBeenCalledWith({
hero: ctrl.hero,
prop: 'foo',
value: 'bar'
});
});
});
-4
View File
@@ -162,10 +162,6 @@ initialization work here. The function is invoked using
{@link auto.$injector#invoke $injector.invoke} which makes it injectable just like a
controller.
<div class="alert alert-success">
**Best Practice:** Prefer using the definition object over returning a function.
</div>
We'll go over a few common examples of directives, then dive deep into the different options
and compilation process.
+147
View File
@@ -0,0 +1,147 @@
@ngdoc overview
@name External Resources
@sortOrder 205
@description
# External Angular 1 Resources
This is a collection of external, 3rd party resources for learning and developing Angular.
## Articles, Videos, and Projects
### Introductory Material
* [10 Reasons Why You Should Use AngularJS](http://www.sitepoint.com/10-reasons-use-angularjs/)
* [10 Reasons Why Developers Should Learn AngularJS](http://wintellect.com/blogs/jlikness/10-reasons-web-developers-should-learn-angularjs)
* [Design Principles of AngularJS (video)](https://www.youtube.com/watch?v=HCR7i5F5L8c)
* [Fundamentals in 60 Minutes (video)](http://www.youtube.com/watch?v=i9MHigUZKEM)
* [For folks with a jQuery background](http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background)
### Specific Topics
#### Application Structure & Style Guides
* [Angular Styleguide](https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md)
* [Architecture, file structure, components, one-way dataflow and best practices](https://github.com/toddmotto/angular-styleguide)
* [When to use directives, controllers or services](http://kirkbushell.me/when-to-use-directives-controllers-or-services-in-angular/)
* [Service vs Factory](http://blog.thoughtram.io/angular/2015/07/07/service-vs-factory-once-and-for-all.html)
#### Testing
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
#### Mobile
* [Angular on Mobile Guide](http://www.ng-newsletter.com/posts/angular-on-mobile.html)
* [Angular and Cordova](http://devgirl.org/2013/06/10/quick-start-guide-phonegap-and-angularjs/)
* [Ionic Framework](http://ionicframework.com/)
#### Deployment
##### General
* **Javascript minification: **[Background](http://thegreenpizza.github.io/2013/05/25/building-minification-safe-angular.js-applications/), [ng-annotate automation tool](https://github.com/olov/ng-annotate)
* **Analytics and Logging:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Angulartics (Analytics)](https://github.com/luisfarzati/angulartics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
* **SEO:** [By hand](http://www.yearofmoo.com/2012/11/angularjs-and-seo.html), [prerender.io](http://prerender.io/), [Brombone](http://www.brombone.com/), [SEO.js](http://getseojs.com/), [SEO4Ajax](http://www.seo4ajax.com/)
##### Server-Specific
* **Django:** [Tutorial](http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework), [Integrating AngularJS with Django](http://django-angular.readthedocs.org/en/latest/integration.html), [Getting Started with Django Rest Framework and AngularJS](http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html)
* **FireBase:** [AngularFire](http://angularfire.com/), [Firebase Foundations for AngularJS](http://blog.watchandcode.com/firebase-foundations/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/developers/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications/), [with Go](https://github.com/GoogleCloudPlatform/appengine-angular-gotodos)
* **Hood.ie:** [60 Minutes to Awesome](http://www.roberthorvick.com/2013/06/30/todomvc-angularjs-hood-ie-60-minutes-to-awesome/)
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
* **Rails: **[Tutorial](http://coderberry.me/blog/2013/04/22/angularjs-on-rails-4-part-1/), [AngularJS with Rails4](https://shellycloud.com/blog/2013/10/how-to-integrate-angularjs-with-rails-4), [angularjs-rails](https://github.com/hiravgandhi/angularjs-rails)
* **PHP: **[Building a RESTful web service](http://blog.brunoscopelliti.com/building-a-restful-web-service-with-angularjs-and-php-more-power-with-resource), [End to End with Laravel 4 (video)](http://www.youtube.com/watch?v=hqAyiqUs93c)
* **Meteor: **[angular-meteor package](https://github.com/Urigo/angular-meteor)
### Other Languages
* [ES6, Webpack, and JSPM Starter Project](https://github.com/AngularClass/NG6-starter)
* [ES6/Typescript Best Practices](https://codepen.io/martinmcwhorter/post/angularjs-1-x-with-typescript-or-es6-best-practices)
* [Dart](https://github.com/angular/angular.dart.tutorial/wiki)
* [CoffeeScript Tutorial](http://www.coffeescriptlove.com/2013/08/angularjs-and-coffeescript-tutorials.html)
### More Topics
* **Security:** [video](https://www.youtube.com/watch?v=18ifoT-Id54)
* **Internationalization and Localization:** [Creating multilingual support](http://www.novanet.no/blog/hallstein-brotan/dates/2013/10/creating-multilingual-support-using-angularjs/)
* **Authentication/Login: **[Google example](https://developers.google.com/+/photohunt/python), [AngularJS Facebook library](https://github.com/pc035860/angular-easyfb), [Facebook example](http://blog.brunoscopelliti.com/facebook-authentication-in-your-angularjs-web-app), [authentication strategy](http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app), [unix-style authorization](http://frederiknakstad.com/authentication-in-single-page-applications-with-angular-js/)
* **Visualization:** [SVG](http://gaslight.co/blog/angular-backed-svgs), [D3.js](http://www.ng-newsletter.com/posts/d3-on-angular.html)
* **Realtime Communication: **[Socket.io](http://www.creativebloq.com/javascript/angularjs-collaboration-board-socketio-2132885), [OmniBinder](https://github.com/jeffbcross/omnibinder)
## Tools
* **Getting Started:** [Comparison of the options for starting a new project](http://www.dancancro.com/comparison-of-angularjs-application-starters/)
* **Debugging:** [Batarang](https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk?hl=en)
* **Editor support:** [Webstorm](http://plugins.jetbrains.com/plugin/6971) (and [video](http://www.youtube.com/watch?v=LJOyrSh1kDU)), [Sublime Text](https://github.com/angular-ui/AngularJS-sublime-package), [Visual Studio](http://madskristensen.net/post/angularjs-intellisense-in-visual-studio-2012)
* **Workflow:** [Yeoman.io](https://github.com/yeoman/generator-angular) and [Angular Yeoman Tutorial](http://www.sitepoint.com/kickstart-your-angularjs-development-with-yeoman-grunt-and-bower/)
## Complementary Libraries
This is a list of libraries that enhance Angular, add common UI components or integrate with other libraries.
You can find a larger list of Angular external libraries at [ngmodules.org](http://ngmodules.org/).
* **Advanced Routing:** [UI-Router](https://github.com/angular-ui/ui-router)
* **Authentication:** [Http Auth Interceptor](https://github.com/witoldsz/angular-http-auth)
* **Internationalization:**
- [angular-translate](http://angular-translate.github.io)
- [angular-gettext](http://angular-gettext.rocketeer.be/)
- [angular-localization](http://doshprompt.github.io/angular-localization/)
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
* **SQL and NoSQL backends:**
- [BreezeJS](http://www.breezejs.com/)
- [AngularFire](http://angularfire.com/)
* **Data Handling**
- Local Storage and session: [ngStorage](https://github.com/gsklee/ngStorage)
- [angular-cache](https://github.com/jmdobry/angular-cache)
- Data Modeling [JS-Data-Angular](https://github.com/js-data/js-data-angular)
* **Fileupload:**
- [ng-file-upload](https://github.com/danialfarid/ng-file-upload)
- [blueimp-fileupload for Angular](https://blueimp.github.io/jQuery-File-Upload/angularjs.html)
* **General UI Libraries:**
- [Angular Material](https://material.angularjs.org/latest/)
- [Angular UI Bootstrap](http://angular-ui.github.io/)
- [AngularStrap for Bootstrap 3](http://mgcrea.github.io/angular-strap/)
- [KendoUI](http://kendo-labs.github.io/angular-kendo/#/)
- [Wijmo](http://wijmo.com/tag/angularjs-2/)
* **Specific UI Elements:**
- [ngInfiniteScroll](https://sroze.github.io/ngInfiniteScroll/)
- [ngTable](https://github.com/esvit/ng-table)
- [Angular UI Grid](http://angular-ui.github.io/grid)
- [Toaster Notifications](https://github.com/jirikavi/AngularJS-Toaster)
- [textAngular Rich Text Editor / contenteditable](http://textangular.com/) (Rich Text Editor /
binding to contenteditable)
- [Angular UI Map (Google Maps)](https://github.com/angular-ui/ui-map)
## General Learning Resources
### Books
* [AngularJS: Up and Running](http://www.amazon.com/AngularJS-Running-Enhanced-Productivity-Structured/dp/1491901942) by Brad Green and Shyam Seshadri
* [Mastering Web App Development](http://www.amazon.com/Mastering-Web-Application-Development-AngularJS/dp/1782161821) by Pawel Kozlowski and Pete Bacon Darwin
* [AngularJS Directives](http://www.amazon.com/AngularJS-Directives-Alex-Vanston/dp/1783280336) by Alex Vanston
* [Recipes With AngularJS](http://www.amazon.co.uk/Recipes-Angular-js-Frederik-Dietz-ebook/dp/B00DK95V48) by Frederik Dietz
* [Developing an AngularJS Edge](http://www.amazon.com/Developing-AngularJS-Edge-Christopher-Hiller-ebook/dp/B00CJLFF8K) by Christopher Hiller
* [ng-book: The Complete Book on AngularJS](http://ng-book.com/) by Ari Lerner
* [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
* [Responsive Web Design with AngularJS](http://www.amazon.com/Responsive-Design-AngularJS-Sandeep-Kumar/dp/178439842X) by Sandeep Kumar Patel
* [Professional AngularJS](http://www.amazon.com/Professional-AngularJS-Valeri-Karpov/dp/1118832078/)
### Videos:
* [egghead.io](http://egghead.io/)
### Courses
* **Free online:**
[thinkster.io](http://thinkster.io),
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1),
[CodeSchool](https://www.codeschool.com/courses/shaping-up-with-angular-js)
* **Paid online:**
[The Angular Course (115 videos that show you how to build a full app)](http://watchandcode.com/courses/angular-course/),
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html),
[WintellectNOW (4 lessons)](http://www.wintellectnow.com/Course/Detail/mastering-angularjs)
* **Paid onsite:**
[angularbootcamp.com](http://angularbootcamp.com/)
+24 -7
View File
@@ -5,10 +5,11 @@
# Filters
A filter formats the value of an expression for display to the user. They can be used in view templates,
controllers or services and it is easy to define your own filter.
Filters format the value of an expression for display to the user. They can be used in view
templates, controllers or services. Angular comes with a collection of
[built-in filters](api/ng/filter), but it is easy to define your own as well.
The underlying API is the {@link ng.$filterProvider `filterProvider`}.
The underlying API is the {@link ng.$filterProvider}.
## Using filters in view templates
@@ -31,6 +32,21 @@ Filters may have arguments. The syntax for this is
E.g. the markup `{{ 1234 | number:2 }}` formats the number 1234 with 2 decimal points using the
{@link ng.filter:number `number`} filter. The resulting value is `1,234.00`.
### When filters are executed
In templates, filters are only executed when their inputs have changed. This is more performant than executing
a filter on each {@link ng.$rootScope.Scope#$digest `$digest`} as is the case with {@link guide/expression expressions}.
There are two exceptions to this rule:
1. In general, this applies only to filters that take [primitive values](https://developer.mozilla.org/docs/Glossary/Primitive)
as inputs. Filters that receive [Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Objects)
as input are executed on each `$digest`, as it would be too costly to track if the inputs have changed.
2. Filters that are marked as `$stateful` are also executed on each $digest.
See {@link guide/filter#stateful-filters Stateful filters} for more information. Note that no Angular
core filters are $stateful.
## Using filters in controllers, services, and directives
@@ -93,8 +109,9 @@ as the first argument. Any filter arguments are passed in as additional argument
function.
The filter function should be a [pure function](http://en.wikipedia.org/wiki/Pure_function), which
means that it should be stateless and idempotent. Angular relies on these properties and executes
the filter only when the inputs to the function change.
means that it should be stateless and idempotent, and not rely for example on other Angular services.
Angular relies on this contract and will by default execute a filter only when the inputs to the function change.
{@link guide/filter#stateful-filters Stateful filters} are possible, but less performant.
<div class="alert alert-warning">
**Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
@@ -141,7 +158,7 @@ text upper-case.
</example>
## Stateful filters
### Stateful filters
It is strongly discouraged to write filters that are stateful, because the execution of those can't
be optimized by Angular, which often leads to performance issues. Many stateful filters can be
@@ -183,4 +200,4 @@ means that it will be executed one or more times during the each `$digest` cycle
## Testing custom filters
See the [phonecat tutorial](http://docs.angularjs.org/tutorial/step_09#test) for an example.
See the [phonecat tutorial](http://docs.angularjs.org/tutorial/step_11#testing) for an example.
+9 -2
View File
@@ -29,6 +29,13 @@ To ensure your Angular application works on IE please consider:
1. Use `ng-style` tags instead of `style="{{ someCss }}"`. The latter works in Chrome and Firefox
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
2. For the `type` attribute of buttons, use `ng-attr-type` tags instead of
`type="{{ someExpression }}"`. If using the latter, Internet Explorer overwrites the expression
2. For the `type` attribute of buttons, use `ng-attr-type` tags instead of
`type="{{ someExpression }}"`. If using the latter, Internet Explorer overwrites the expression
with `type="submit"` before Angular has a chance to interpolate it.
3. For the `value` attribute of progress, use `ng-attr-value` tags instead of
`value="{{ someExpression}}"`. If using the latter, Internet Explorer overwrites the expression
with `value="0"` before Angular has a chance to interpolate it.
4. For the `placeholder` attribute of textarea, use `ng-attr-placeholder` tags instead
of `placeholder="{{ someExpression }}"`. If using the latter, Internet Explorer will error
on accessing the `nodeValue` on a parentless `TextNode` in Internet Explorer 10 & 11
(see [issue 5025](https://github.com/angular/angular.js/issues/5025)).
+40 -107
View File
@@ -2,22 +2,21 @@
@name Developer Guide
@description
# Guide to AngularJS Documentation
# Guide to Angular 1 Documentation
Everything you need to know about AngularJS
On this page, you will find a list of official Angular resources on various topics.
* {@link guide/introduction What is AngularJS?}
Just starting out with Angular 1? Try working through our step by step tutorial or try
building on our seed project.
* {@link tutorial/index Official Angular 1 Tutorial}
* [Angular Seed](https://github.com/angular/angular-seed)
Ready to find out more about Angular 1?
* {@link guide/introduction What is Angular 1?}
* {@link guide/concepts Conceptual Overview}
## Tutorials
* {@link tutorial/index Official AngularJS Tutorial}
* [10 Reasons Why You Should Use AngularJS](http://www.sitepoint.com/10-reasons-use-angularjs/)
* [10 Reasons Why Developers Should Learn AngularJS](http://wintellect.com/blogs/jlikness/10-reasons-web-developers-should-learn-angularjs)
* [Design Principles of AngularJS (video)](https://www.youtube.com/watch?v=HCR7i5F5L8c)
* [Fundamentals in 60 Minutes (video)](http://www.youtube.com/watch?v=i9MHigUZKEM)
* [For folks with a jQuery background](http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background)
## Core Concepts
### Templates
@@ -26,134 +25,68 @@ In Angular applications, you move the job of filling page templates with data fr
* {@link guide/databinding Data binding}
* {@link guide/expression Expressions}
* {@link guide/interpolation Interpolation}
* {@link guide/directive Directives}
* {@link ngRoute.$route Views and routes (see the example)}
* {@link guide/filter Filters}
* {@link guide/forms Forms} and [Concepts of AngularJS Forms](http://mrbool.com/the-concepts-of-angularjs-forms/29117)
* {@link guide/compiler HTML compiler}
* {@link guide/forms Forms}
### Application Structure
* **Blog post: **[When to use directives, controllers or services](http://kirkbushell.me/when-to-use-directives-controllers-or-services-in-angular/)
* **App wiring:** {@link guide/di Dependency injection}
* **Exposing model to templates:** {@link guide/scope Scopes}
* **Bootstrap:** {@link guide/bootstrap Bootstrapping an app}
* **Communicating with servers:** {@link ng.$http $http}, {@link ngResource.$resource $resource}
### Other AngularJS Features
### Other Features
* **Animation:** {@link guide/animations Core concepts}, {@link ngAnimate ngAnimate API}, and [Animation in AngularJS 1.2](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html)
* **Animation:** {@link guide/animations Core concepts}, {@link ngAnimate ngAnimate API}
* **Security:** {@link guide/security Security Docs}, {@link ng.$sce Strict Contextual Escaping}, {@link ng.directive:ngCsp Content Security Policy}, {@link ngSanitize.$sanitize $sanitize}, [video](https://www.youtube.com/watch?v=18ifoT-Id54)
* **Internationalization and Localization:** {@link guide/i18n Angular Guide to i18n and l10n}, {@link ng.filter:date date filter}, {@link ng.filter:currency currency filter}, [Creating multilingual support](http://www.novanet.no/blog/hallstein-brotan/dates/2013/10/creating-multilingual-support-using-angularjs/)
* **Mobile:** {@link ngTouch Touch events}
* **Touch events:** {@link ngTouch Touch events}
* **Accessibility:** {@link guide/accessibility ngAria}
### Testing
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/unit-testing Unit testing}, {@link guide/services#unit-testing Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
* **Scenario testing:** [Protractor](https://github.com/angular/protractor)
* **Unit testing:** [Karma](http://karma-runner.github.io), {@link guide/unit-testing Unit testing}, {@link guide/services#unit-testing Testing services},
* **End-to-End Testing:** [Protractor](https://github.com/angular/protractor), {@link guide/e2e-testing e2e testing guide}
## Specific Topics
* **Login: **[Google example](https://developers.google.com/+/photohunt/python), [AngularJS Facebook library](https://github.com/pc035860/angular-easyfb), [Facebook example](http://blog.brunoscopelliti.com/facebook-authentication-in-your-angularjs-web-app), [authentication strategy](http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app), [unix-style authorization](http://frederiknakstad.com/authentication-in-single-page-applications-with-angular-js/)
* **Mobile:** [Angular on Mobile Guide](http://www.ng-newsletter.com/posts/angular-on-mobile.html), [PhoneGap](http://devgirl.org/2013/06/10/quick-start-guide-phonegap-and-angularjs/)
* **Other Languages:** [CoffeeScript](http://www.coffeescriptlove.com/2013/08/angularjs-and-coffeescript-tutorials.html), [Dart](https://github.com/angular/angular.dart.tutorial/wiki)
* **Realtime: **[Socket.io](http://www.creativebloq.com/javascript/angularjs-collaboration-board-socketio-2132885), [OmniBinder](https://github.com/jeffbcross/omnibinder)
* **Visualization:** [SVG](http://gaslight.co/blog/angular-backed-svgs), [D3.js](http://www.ng-newsletter.com/posts/d3-on-angular.html)
* **Local Storage and session:** [ngStorage](https://github.com/gsklee/ngStorage)
## Community Resources
## Tools
We have set up a guide to many resources provided by the community, where you can find lots
of additional information and material on these topics, a list of complimentary libraries, and much more.
* **Getting Started:** [Comparison of the options for starting a new project](http://www.dancancro.com/comparison-of-angularjs-application-starters/)
* **Debugging:** [Batarang](https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk?hl=en)
* **Testing:** [Karma](http://karma-runner.github.io), [Protractor](https://github.com/angular/protractor)
* **Editor support:** [Webstorm](http://plugins.jetbrains.com/plugin/6971) (and [video](http://www.youtube.com/watch?v=LJOyrSh1kDU)), [Sublime Text](https://github.com/angular-ui/AngularJS-sublime-package), [Visual Studio](http://madskristensen.net/post/angularjs-intellisense-in-visual-studio-2012)
* **Workflow:** [Yeoman.io](https://github.com/yeoman/generator-angular) and [Angular Yeoman Tutorial](http://www.sitepoint.com/kickstart-your-angularjs-development-with-yeoman-grunt-and-bower/)
* {@link guide/external-resources External Angular 1 resources}
## Complementary Libraries
This is a short list of libraries with specific support and documentation for working with Angular. You can find a full list of all known Angular external libraries at [ngmodules.org](http://ngmodules.org/).
* **Internationalization:** [angular-translate](http://angular-translate.github.io), [angular-gettext](http://angular-gettext.rocketeer.be/), [angular-localization](http://doshprompt.github.io/angular-localization/)
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
* **SQL and NoSQL backends:** [BreezeJS](http://www.breezejs.com/), [AngularFire](http://angularfire.com/)
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/), [ngTagsInput](https://github.com/mbenford/ngTagsInput)
* **Advanced Routing:** [UI-Router](https://github.com/angular-ui/ui-router)
* **Maps:** [UI-Map (Google Maps)](https://github.com/angular-ui/ui-map)
## Deployment
### General
* **Docs Page:** {@link guide/production Running an AngularJS App in Production}
* **Javascript minification: **[Background](http://thegreenpizza.github.io/2013/05/25/building-minification-safe-angular.js-applications/), [ng-annotate automation tool](https://github.com/olov/ng-annotate)
* **Analytics and Logging:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Angulartics (Analytics)](https://github.com/luisfarzati/angulartics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
* **SEO:** [By hand](http://www.yearofmoo.com/2012/11/angularjs-and-seo.html), [prerender.io](http://prerender.io/), [Brombone](http://www.brombone.com/), [SEO.js](http://getseojs.com/), [SEO4Ajax](http://www.seo4ajax.com/)
### Server-Specific
* **Django:** [Tutorial](http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework), [Integrating AngularJS with Django](http://django-angular.readthedocs.org/en/latest/integration.html), [Getting Started with Django Rest Framework and AngularJS](http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html)
* **FireBase:** [AngularFire](http://angularfire.com/), [Firebase Foundations for AngularJS](http://blog.watchandcode.com/firebase-foundations/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/developers/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications/), [with Go](https://github.com/GoogleCloudPlatform/appengine-angular-gotodos)
* **Hood.ie:** [60 Minutes to Awesome](http://www.roberthorvick.com/2013/06/30/todomvc-angularjs-hood-ie-60-minutes-to-awesome/)
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
* **Rails: **[Tutorial](http://coderberry.me/blog/2013/04/22/angularjs-on-rails-4-part-1/), [AngularJS with Rails4](https://shellycloud.com/blog/2013/10/how-to-integrate-angularjs-with-rails-4), [angularjs-rails](https://github.com/hiravgandhi/angularjs-rails)
* **PHP: **[Building a RESTful web service](http://blog.brunoscopelliti.com/building-a-restful-web-service-with-angularjs-and-php-more-power-with-resource), [End to End with Laravel 4 (video)](http://www.youtube.com/watch?v=hqAyiqUs93c)
* **Meteor: **[angular-meteor package](https://github.com/Urigo/angular-meteor)
## Learning Resources
### Books
* [AngularJS: Up and Running](http://www.amazon.com/AngularJS-Running-Enhanced-Productivity-Structured/dp/1491901942) by Brad Green and Shyam Seshadri
* [Mastering Web App Development](http://www.amazon.com/Mastering-Web-Application-Development-AngularJS/dp/1782161821) by Pawel Kozlowski and Pete Bacon Darwin
* [AngularJS Directives](http://www.amazon.com/AngularJS-Directives-Alex-Vanston/dp/1783280336) by Alex Vanston
* [Recipes With AngularJS](http://www.amazon.co.uk/Recipes-Angular-js-Frederik-Dietz-ebook/dp/B00DK95V48) by Frederik Dietz
* [Developing an AngularJS Edge](http://www.amazon.com/Developing-AngularJS-Edge-Christopher-Hiller-ebook/dp/B00CJLFF8K) by Christopher Hiller
* [ng-book: The Complete Book on AngularJS](http://ng-book.com/) by Ari Lerner
* [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
* [Responsive Web Design with AngularJS](http://www.amazon.com/Responsive-Design-AngularJS-Sandeep-Kumar/dp/178439842X) by Sandeep Kumar Patel
* [Professional AngularJS](http://www.amazon.com/Professional-AngularJS-Valeri-Karpov/dp/1118832078/)
### Videos:
* [egghead.io](http://egghead.io/)
* [Angular on YouTube](http://youtube.com/angularjs)
* [Firebase Foundations for AngularJS](http://blog.watchandcode.com/firebase-foundations/)
### Courses
* **Free online:**
[thinkster.io](http://thinkster.io),
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1),
[CodeSchool](https://www.codeschool.com/courses/shaping-up-with-angular-js)
* **Paid online:**
[The Angular Course (115 videos that show you how to build a full app)](http://watchandcode.com/courses/angular-course/),
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html),
[WintellectNOW (4 lessons)](http://www.wintellectnow.com/Course/Detail/mastering-angularjs)
* **Paid onsite:**
[angularbootcamp.com](http://angularbootcamp.com/)
## Getting Help
The recipe for getting help on your unique issue is to create an example that could work (even if it doesn't) in a shareable example on [Plunker](http://plnkr.co/), [JSFiddle](http://jsfiddle.net/), or similar site and then post to one of the following:
* [Stackoverflow.com](http://stackoverflow.com/search?q=angularjs)
* [AngularJS mailing list](https://groups.google.com/forum/#!forum/angular)
* [AngularJS IRC channel](http://webchat.freenode.net/?channels=angularjs&uio=d4)
* [Angular 1 mailing list](https://groups.google.com/forum/#!forum/angular)
* [Angular 1 IRC channel](http://webchat.freenode.net/?channels=angularjs&uio=d4)
## Social Channels
## Official Communications
* **Daily updates:** [Google+](https://plus.google.com/u/0/+AngularJS) or [Twitter](https://twitter.com/angularjs)
* **Weekly newsletter:** [ng-newsletter](http://www.ng-newsletter.com/)
* **Meetups: **[meetup.com](http://www.meetup.com/find/?keywords=angularJS&radius=Infinity&userFreeform=San+Francisco%2C+CA&mcId=z94108&mcName=San+Francisco%2C+CA&sort=member_count&eventFilter=mysugg)
* **Official news and releases: **[AngularJS Blog](http://blog.angularjs.org/)
Official announcements, news and releases are posted to our blog, G+ and Twitter:
## Contributing to AngularJS
* [Angular Blog](http://blog.angularjs.org/)
* [Google+](https://plus.google.com/u/0/+AngularJS)
* [Twitter](https://twitter.com/angularjs)
* [Angular on YouTube](http://youtube.com/angularjs)
Though we have a core group of core contributors at Google, Angular is an open source project with hundreds of contributors. We'd love you to be one of them. When you're ready, please read the {@link misc/contribute Guide for contributing to AngularJS}.
## Contributing to Angular 1
## Final Bits
Though we have a core group of core contributors at Google, Angular is an open source project with hundreds of contributors.
We'd love you to be one of them. When you're ready, please read the {@link misc/contribute Guide for contributing to Angular}.
Didn't find what you're looking for here? Check out [AngularJS-Learning](https://github.com/jmcunningham/AngularJS-Learning) for an even more comprehensive list of links to videos, tutorials, and blog posts.
## Something Missing?
If you have awesome AngularJS resources that belong on this page, please tell us about them on [Google+](https://plus.google.com/u/0/+AngularJS) or [Twitter](https://twitter.com/angularjs).
Didn't find what you're looking for here? Check out the {@link guide/external-resources External Angular 1 resources guide}.
If you have awesome Angular 1 resources that belong on that page, please tell us about them on
[Google+](https://plus.google.com/u/0/+AngularJS) or [Twitter](https://twitter.com/angularjs).
+15 -2
View File
@@ -37,7 +37,7 @@ would be lost, because the browser ignores the attribute value.
In the following example, the interpolation information would be ignored and the browser would simply
interpret the attribute as present, meaning that the button would always be disabled.
```html
```html
Disabled: <input type="checkbox" ng-model="isDisabled" />
<button disabled="{{isDisabled}}">Disabled</button>
```
@@ -49,7 +49,7 @@ For this reason, Angular provides special `ng`-prefixed directives for the follo
These directives take an expression inside the attribute, and set the corresponding boolean attribute
to true when the expression evaluates to truthy.
```html
```html
Disabled: <input type="checkbox" ng-model="isDisabled" />
<button ng-disabled="isDisabled">Disabled</button>
```
@@ -104,7 +104,20 @@ can be used with `ngAttr` instead. The following is a list of known problematic
- **size** in `<select>` elements (see [issue 1619](https://github.com/angular/angular.js/issues/1619))
- **placeholder** in `<textarea>` in Internet Explorer 10/11 (see [issue 5025](https://github.com/angular/angular.js/issues/5025))
- **type** in `<button>` in Internet Explorer 11 (see [issue 14117](https://github.com/angular/angular.js/issues/5025))
- **value** in `<progress>` in Internet Explorer = 11 (see [issue 7218](https://github.com/angular/angular.js/issues/7218))
## Known Issues
### Dynamically changing an interpolated value
You should avoid dynamically changing the content of an interpolated string (e.g. attribute value
or text node). Your changes are likely to be overwriten, when the original string gets evaluated.
This restriction applies to both directly changing the content via JavaScript or indirectly using a
directive.
For example, you should not use interpolation in the value of the `style` attribute (e.g.
`style="color: {{ 'orange' }}; font-weight: {{ 'bold' }};"`) **and** at the same time use a
directive that changes the content of that attributte, such as `ngStyle`.
### Embedding interpolation markup inside expressions
+2 -2
View File
@@ -75,8 +75,8 @@ that you break your application to multiple modules like this:
* And an application level module which depends on the above modules and contains any
initialization code.
You can find a community
[style guide](https://github.com/johnpapa/angular-styleguide) to help yourself when application grows.
You can find a community [style guide](https://github.com/johnpapa/angular-styleguide) to help
yourself when application grows.
The above is a suggestion. Tailor it to your needs.
+2
View File
@@ -3,6 +3,8 @@
@sortOrder 410
@description
# Unit Testing
JavaScript is a dynamically typed language which comes with great power of expression, but it also
comes with almost no help from the compiler. For this reason we feel very strongly that any code
written in JavaScript needs to come with a strong set of tests. We have built many features into
+1 -1
View File
@@ -53,7 +53,7 @@ minified AngularJS files:
```shell
# Clone your Github repository:
git clone "git@github.com:<github username>/angular.js.git"
git clone https://github.com/<github username>/angular.js.git
# Go to the AngularJS directory:
cd angular.js
+2 -1
View File
@@ -100,7 +100,8 @@ The set of files included in each version directory are:
assistive technologies. ({@link module:ngAria API docs})
* __`angular-cookies.js`__ — A convenient wrapper for reading and writing browser cookies.
({@link module:ngCookies API docs})
<!-- * __`angular-message-format.js`__ — ({@link module:ngMessageFormat API docs}) -->
* __`angular-message-format.js`__ — Enhanced support for pluralization and gender specific
messages in interpolated text. ({@link module:ngMessageFormat API docs})
* __`angular-messages.js`__ — Enhanced support for displaying validation messages.
({@link module:ngMessages API docs})
* __`angular-parse-ext.js`__ — Allow Unicode characters in identifiers inside Angular expressions.
+6 -6
View File
@@ -199,16 +199,16 @@ Voilà! The resulting output should look the same, but let's see what we have ga
# Testing
Although we have combined our controller with a template into a component, we still can (and should)
unit test the controller separately, since this is where are application logic and data reside.
unit test the controller separately, since this is where our application logic and data reside.
In order to retrieve and instantiate a component's controller, Angular provides the
{@link ngMock.$componentController $componentController} service.
<div class="alert alert-info">
The `$controller` service that we used in the previous step, can only instantiate controllers that
where registered by name, using the `.controller()` method. We could have registered our component
controller this way too, if we wanted to. Instead, we chose to define it inline &mdash; inside the
CDO &mdash; to keep things localized, but either way works equally well.
The `$controller` service that we used in the previous step can only instantiate controllers that
were registered by name, using the `.controller()` method. We could have registered our component
controller this way, too, if we wanted to. Instead, we chose to define it inline &mdash; inside
the CDO &mdash; to keep things localized, but either way works equally well.
</div>
<br />
@@ -268,7 +268,7 @@ Same as before, execute `npm test` to run the tests and then watch the files for
# Summary
You have learned how to organize your application and presentation logic into isolated reusable
You have learned how to organize your application and presentation logic into isolated, reusable
components. Let's go to {@link step_04 step 4} to learn how to organize our code in directories and
files, so it remains easy to locate as our application grows.
+2 -2
View File
@@ -119,8 +119,8 @@ Yeah, you guessed it: There is a better way!
Each feature/section, will declare its own module and have all related entities registered there.
The main module (`phonecatApp`) will declare a dependency on each feature/section module. Now,
all it takes to reuse the same code on new project is copying the feature directory over and adding
the feature module as a dependency in the new project's main module.
all it takes to reuse the same code on a new project is copying the feature directory over and
adding the feature module as a dependency in the new project's main module.
Here is what our `phoneList` feature will look like after this change:
Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

After

Width:  |  Height:  |  Size: 120 KiB

@@ -27,17 +27,17 @@
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1458"
inkscape:window-width="1460"
inkscape:window-height="841"
id="namedview3683"
showgrid="false"
inkscape:zoom="0.9721519"
inkscape:cx="318.71745"
inkscape:cy="345.625"
inkscape:window-x="70"
inkscape:window-x="68"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g3434"
inkscape:current-layer="g3440"
inkscape:snap-bbox="false"
inkscape:bbox-nodes="false">
<inkscape:grid
@@ -52,7 +52,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@@ -873,10 +873,10 @@
<image
sodipodi:absref="C:\Datafiles\Dev\AngularJS\angular.js\images\docs\tutorial\tutorial_10.svg\image10.png"
xlink:href="image10.png"
id="image3650"
height="250"
transform="translate(520,28)"
width="217"
transform="translate(520,28)" />
height="250"
id="image3650" />
</g>
<use
id="use3646"
@@ -1197,13 +1197,13 @@
sodipodi:linespacing="125%"
id="text4962"
y="24.633343"
x="162.71861"
x="159.5186"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:125%;font-family:'Courier New';-inkscape-font-specification:'Courier New, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="24.633343"
x="162.71861"
id="tspan4964"
sodipodi:role="line">phoneList component</tspan></text>
sodipodi:role="line"
id="tspan3919"
x="159.5186"
y="24.633343">phoneDetail component</tspan></text>
</g>
<g
id="g5196"
@@ -1354,7 +1354,7 @@
sodipodi:role="line"
id="tspan8160"
x="384.30206"
y="-28.56875"></tspan></text>
y="-28.56875" /></text>
</g>
<g
id="g7990"

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

@@ -15,7 +15,7 @@
id="svg3336"
inkscape:version="0.91 r13725"
sodipodi:docname="tutorial_12.svg"
inkscape:export-filename="C:\Datafiles\Dev\AngularJS\angular.js\docs\img\tutorial\tutorial_03.png"
inkscape:export-filename="C:\Datafiles\Dev\AngularJS\angular.js\docs\img\tutorial\tutorial_12_2.png"
inkscape:export-xdpi="65"
inkscape:export-ydpi="65">
<sodipodi:namedview
@@ -27,14 +27,14 @@
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1458"
inkscape:window-width="1460"
inkscape:window-height="841"
id="namedview3683"
showgrid="false"
inkscape:zoom="0.9721519"
inkscape:cx="327.82637"
inkscape:cy="345.625"
inkscape:window-x="70"
inkscape:window-x="68"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g3434"
@@ -52,7 +52,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@@ -942,7 +942,7 @@
x="0"
y="0"
style="fill:#ffffff"
inkscape:export-filename="C:\Datafiles\Dev\AngularJS\angular.js\docs\img\tutorial\tutorial_12.png"
inkscape:export-filename="C:\Datafiles\Dev\AngularJS\angular.js\docs\img\tutorial\tutorial_12_2.png"
inkscape:export-xdpi="66"
inkscape:export-ydpi="66" />
<text
@@ -1319,13 +1319,13 @@
sodipodi:linespacing="125%"
id="text4962"
y="24.633343"
x="162.71861"
x="159.5186"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:125%;font-family:'Courier New';-inkscape-font-specification:'Courier New, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="24.633343"
x="162.71861"
id="tspan4964"
sodipodi:role="line">phoneList component</tspan></text>
sodipodi:role="line"
id="tspan3638"
x="159.5186"
y="24.633343">phoneDetail component</tspan></text>
</g>
<g
id="g5196"
@@ -1480,7 +1480,7 @@
y="37.03125"
x="384.30206"
id="tspan8160"
sodipodi:role="line"></tspan></text>
sodipodi:role="line" /></text>
<g
id="g7990"
transform="translate(0,316.8)">

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

+2 -15
View File
@@ -1,18 +1,5 @@
'use strict';
var angularFiles = require('./angularFiles');
var sharedConfig = require('./karma-shared.conf');
var karmaConfigFactory = require('./karma-jquery.conf-factory');
module.exports = function(config) {
sharedConfig(config, {testName: 'AngularJS: jQuery', logFile: 'karma-jquery.log'});
config.set({
files: angularFiles.mergeFilesFor('karmaJqueryOld'),
exclude: angularFiles.mergeFilesFor('karmaJqueryExclude'),
junitReporter: {
outputFile: 'test_out/jquery.xml',
suite: 'jQuery'
}
});
};
module.exports = karmaConfigFactory('2.1');
+5
View File
@@ -0,0 +1,5 @@
'use strict';
var karmaConfigFactory = require('./karma-jquery.conf-factory');
module.exports = karmaConfigFactory('2.2');
+25
View File
@@ -0,0 +1,25 @@
'use strict';
var angularFiles = require('./angularFiles');
var sharedConfig = require('./karma-shared.conf');
module.exports = function (version) {
version = version || '';
return function(config) {
sharedConfig(config, {
testName: 'AngularJS: jQuery' + (version ? ' ' + version : ''),
logFile: 'karma-jquery' + version + '.log'
});
config.set({
files: angularFiles.mergeFilesFor('karmaJquery' + version),
exclude: angularFiles.mergeFilesFor('karmaJqueryExclude'),
junitReporter: {
outputFile: 'test_out/jquery.xml',
suite: 'jQuery'
}
});
};
};
+2 -15
View File
@@ -1,18 +1,5 @@
'use strict';
var angularFiles = require('./angularFiles');
var sharedConfig = require('./karma-shared.conf');
var karmaConfigFactory = require('./karma-jquery.conf-factory');
module.exports = function(config) {
sharedConfig(config, {testName: 'AngularJS: jQuery', logFile: 'karma-jquery.log'});
config.set({
files: angularFiles.mergeFilesFor('karmaJquery'),
exclude: angularFiles.mergeFilesFor('karmaJqueryExclude'),
junitReporter: {
outputFile: 'test_out/jquery.xml',
suite: 'jQuery'
}
});
};
module.exports = karmaConfigFactory();
+1 -1
View File
@@ -7,7 +7,7 @@ module.exports = function(config) {
sharedConfig(config, {testName: 'AngularJS: modules', logFile: 'karma-modules.log'});
config.set({
files: angularFiles.mergeFilesFor('karmaModules', 'angularSrcModules'),
files: angularFiles.mergeFilesFor('karmaModules'),
junitReporter: {
outputFile: 'test_out/modules.xml',
-7
View File
@@ -1,7 +0,0 @@
"use strict";
var isOnlineAsync = require('is-online');
isOnlineAsync(function(err, isOnline) {
console.log(isOnline ? 'online' : 'offline');
});
+63 -53
View File
@@ -1,16 +1,18 @@
'use strict';
var childProcess = require('child_process');
var fs = require('fs');
var path = require('path');
var shell = require('shelljs');
var semver = require('semver');
var _ = require('lodash');
var process = require('process');
// We are only interested in whether this environment variable exists, hence the !!
var NO_REMOTE_REQUESTS = !!process.env['NG1_BUILD_NO_REMOTE_VERSION_REQUESTS'];
var versionSource = NO_REMOTE_REQUESTS ? 'local' : 'remote';
var currentPackage, previousVersions, cdnVersion, gitRepoInfo;
var connectivityStatus = childProcess.execSync('node is-online.js', { cwd: __dirname, encoding: 'utf8' }).trim();
var isOnline = connectivityStatus == 'online';
console.log('Running ' + connectivityStatus);
/**
* Load information about this project from the package.json
@@ -99,72 +101,68 @@ var getTaggedVersion = function() {
* @return {Array.<SemVer>} The collection of previous versions
*/
var getPreviousVersions = function() {
// always use the remote tags as the local clone might
// If we are allowing remote requests then use the remote tags as the local clone might
// not contain all commits when cloned with git clone --depth=...
// Needed e.g. for Travis
// Otherwise just use the tags in the local repository
var repo_url = currentPackage.repository.url;
if (isOnline) {
console.log('looking up remote git tags...');
var tagResults = shell.exec('git ls-remote --tags ' + repo_url,
{silent: true});
if (tagResults.code === 0) {
return _(tagResults.output.match(/v[0-9].*[0-9]$/mg))
.map(function(tag) {
var version = semver.parse(tag);
return version;
})
.filter()
.map(function(version) {
// angular.js didn't follow semantic version until 1.20rc1
if ((version.major === 1 && version.minor === 0 && version.prerelease.length > 0) || (version.major === 1 && version.minor === 2 && version.prerelease[0] === 'rc1')) {
version.version = [version.major, version.minor, version.patch].join('.') + version.prerelease.join('');
version.raw = 'v' + version.version;
}
version.docsUrl = 'http://code.angularjs.org/' + version.version + '/docs';
// Versions before 1.0.2 had a different docs folder name
if (version.major < 1 || (version.major === 1 && version.minor === 0 && version.patch < 2)) {
version.docsUrl += '-' + version.version;
version.isOldDocsUrl = true;
}
return version;
})
.sort(semver.compare)
.value();
}
var query = NO_REMOTE_REQUESTS ? 'git tag' : 'git ls-remote --tags ' + repo_url;
var tagResults = shell.exec(query, {silent: true});
if (tagResults.code === 0) {
return _(tagResults.output.match(/v[0-9].*[0-9]$/mg))
.map(function(tag) {
var version = semver.parse(tag);
return version;
})
.filter()
.map(function(version) {
// angular.js didn't follow semantic version until 1.20rc1
if ((version.major === 1 && version.minor === 0 && version.prerelease.length > 0) || (version.major === 1 && version.minor === 2 && version.prerelease[0] === 'rc1')) {
version.version = [version.major, version.minor, version.patch].join('.') + version.prerelease.join('');
version.raw = 'v' + version.version;
}
version.docsUrl = 'http://code.angularjs.org/' + version.version + '/docs';
// Versions before 1.0.2 had a different docs folder name
if (version.major < 1 || (version.major === 1 && version.minor === 0 && version.patch < 2)) {
version.docsUrl += '-' + version.version;
version.isOldDocsUrl = true;
}
return version;
})
.sort(semver.compare)
.value();
} else {
console.log('-- skipping remote git tag lookup');
return [];
}
return [];
};
var getCdnVersion = function() {
if (isOnline) {
console.log('searching for CDN versions...');
return _(previousVersions)
.filter(function(tag) {
return semver.satisfies(tag, currentPackage.branchVersion);
})
.reverse()
.reduce(function(cdnVersion, version) {
if (!cdnVersion) {
return _(previousVersions)
.filter(function(tag) {
return semver.satisfies(tag, currentPackage.branchVersion);
})
.reverse()
.reduce(function(cdnVersion, version) {
if (!cdnVersion) {
if (NO_REMOTE_REQUESTS) {
// We do not want to make any remote calls to the CDN so just use the most recent version
cdnVersion = version;
} else {
// Note: need to use shell.exec and curl here
// as version-infos returns its result synchronously...
var cdnResult = shell.exec('curl http://ajax.googleapis.com/ajax/libs/angularjs/' + version + '/angular.min.js ' +
'--head --write-out "%{http_code}" -o /dev/null -silent',
'--head --write-out "%{http_code}" -silent',
{silent: true});
if (cdnResult.code === 0) {
var statusCode = cdnResult.output.trim();
// --write-out appends its content to the general request response, so extract it
var statusCode = cdnResult.output.split('\n').pop().trim();
if (statusCode === '200') {
console.log('-- found CDN version ' + version);
cdnVersion = version;
}
}
}
return cdnVersion;
}, null);
} else {
console.log('-- skipping CDN version lookup');
}
}
return cdnVersion;
}, null);
};
/**
@@ -211,8 +209,20 @@ var getSnapshotVersion = function() {
return version;
};
exports.currentPackage = currentPackage = getPackage();
exports.gitRepoInfo = gitRepoInfo = getGitRepoInfo();
exports.previousVersions = previousVersions = getPreviousVersions();
exports.cdnVersion = cdnVersion = getCdnVersion();
exports.currentVersion = getTaggedVersion() || getSnapshotVersion();
if (NO_REMOTE_REQUESTS) {
console.log('==============================================================================================');
console.log('Running with no remote requests for version data:');
console.log(' - this is due to the "NG1_BUILD_NO_REMOTE_VERSION_REQUESTS" environment variable being defined.');
console.log(' - be aware that the generated docs may not have valid or the most recent version information.');
console.log('==============================================================================================');
}
console.log('CDN version (' + versionSource + '):', cdnVersion ? cdnVersion.raw : 'No version found.');
console.log('Current version (' + versionSource + '):', exports.currentVersion.raw);
+89 -508
View File
@@ -7,10 +7,10 @@
"version": "3.3.6"
},
"express": {
"version": "4.13.4",
"version": "4.14.0",
"dependencies": {
"accepts": {
"version": "1.2.13",
"version": "1.3.3",
"dependencies": {
"mime-types": {
"version": "2.1.11",
@@ -21,7 +21,7 @@
}
},
"negotiator": {
"version": "0.5.3"
"version": "0.6.1"
}
}
},
@@ -35,7 +35,7 @@
"version": "1.0.2"
},
"cookie": {
"version": "0.1.5"
"version": "0.3.1"
},
"cookie-signature": {
"version": "1.0.6"
@@ -51,6 +51,9 @@
"depd": {
"version": "1.1.0"
},
"encodeurl": {
"version": "1.0.1"
},
"escape-html": {
"version": "1.0.3"
},
@@ -58,8 +61,11 @@
"version": "1.7.0"
},
"finalhandler": {
"version": "0.4.1",
"version": "0.5.0",
"dependencies": {
"statuses": {
"version": "1.3.0"
},
"unpipe": {
"version": "1.0.0"
}
@@ -89,33 +95,36 @@
"version": "0.1.7"
},
"proxy-addr": {
"version": "1.0.10",
"version": "1.1.2",
"dependencies": {
"forwarded": {
"version": "0.1.0"
},
"ipaddr.js": {
"version": "1.0.5"
"version": "1.1.1"
}
}
},
"qs": {
"version": "4.0.0"
"version": "6.2.0"
},
"range-parser": {
"version": "1.0.3"
"version": "1.2.0"
},
"send": {
"version": "0.13.1",
"version": "0.14.1",
"dependencies": {
"destroy": {
"version": "1.0.4"
},
"http-errors": {
"version": "1.3.1",
"version": "1.5.0",
"dependencies": {
"inherits": {
"version": "2.0.1"
},
"setprototypeof": {
"version": "1.0.1"
}
}
},
@@ -126,15 +135,15 @@
"version": "0.7.1"
},
"statuses": {
"version": "1.2.1"
"version": "1.3.0"
}
}
},
"serve-static": {
"version": "1.10.2"
"version": "1.11.1"
},
"type-is": {
"version": "1.6.12",
"version": "1.6.13",
"dependencies": {
"media-typer": {
"version": "0.3.0"
@@ -153,7 +162,7 @@
"version": "1.0.0"
},
"vary": {
"version": "1.0.1"
"version": "1.1.0"
}
}
},
@@ -169,16 +178,19 @@
}
},
"rimraf": {
"version": "2.5.2",
"version": "2.5.3",
"dependencies": {
"glob": {
"version": "7.0.3",
"version": "7.0.5",
"dependencies": {
"fs.realpath": {
"version": "1.0.0"
},
"inflight": {
"version": "1.0.4",
"version": "1.0.5",
"dependencies": {
"wrappy": {
"version": "1.0.1"
"version": "1.0.2"
}
}
},
@@ -186,10 +198,10 @@
"version": "2.0.1"
},
"minimatch": {
"version": "3.0.0",
"version": "3.0.2",
"dependencies": {
"brace-expansion": {
"version": "1.1.4",
"version": "1.1.5",
"dependencies": {
"balanced-match": {
"version": "0.4.1"
@@ -205,7 +217,7 @@
"version": "1.3.3",
"dependencies": {
"wrappy": {
"version": "1.0.1"
"version": "1.0.2"
}
}
},
@@ -337,7 +349,7 @@
}
},
"browser-resolve": {
"version": "1.11.1",
"version": "1.11.2",
"dependencies": {
"resolve": {
"version": "1.1.7"
@@ -433,13 +445,13 @@
"version": "4.0.0",
"dependencies": {
"bn.js": {
"version": "4.11.3"
"version": "4.11.4"
},
"browserify-rsa": {
"version": "4.0.1"
},
"elliptic": {
"version": "6.2.3",
"version": "6.3.1",
"dependencies": {
"brorand": {
"version": "1.0.5"
@@ -453,7 +465,7 @@
"version": "5.0.0",
"dependencies": {
"asn1.js": {
"version": "4.6.0",
"version": "4.6.2",
"dependencies": {
"minimalistic-assert": {
"version": "1.0.0"
@@ -482,10 +494,10 @@
"version": "4.0.0",
"dependencies": {
"bn.js": {
"version": "4.11.3"
"version": "4.11.4"
},
"elliptic": {
"version": "6.2.3",
"version": "6.3.1",
"dependencies": {
"brorand": {
"version": "1.0.5"
@@ -518,7 +530,7 @@
"version": "5.0.2",
"dependencies": {
"bn.js": {
"version": "4.11.3"
"version": "4.11.4"
},
"miller-rabin": {
"version": "4.0.0",
@@ -537,7 +549,7 @@
"version": "4.0.0",
"dependencies": {
"bn.js": {
"version": "4.11.3"
"version": "4.11.4"
},
"browserify-rsa": {
"version": "4.0.1"
@@ -546,7 +558,7 @@
"version": "5.0.0",
"dependencies": {
"asn1.js": {
"version": "4.6.0",
"version": "4.6.2",
"dependencies": {
"minimalistic-assert": {
"version": "1.0.0"
@@ -586,7 +598,7 @@
"version": "1.3.9",
"dependencies": {
"JSONStream": {
"version": "1.1.1",
"version": "1.1.3",
"dependencies": {
"jsonparse": {
"version": "1.2.0"
@@ -611,10 +623,10 @@
"version": "4.5.3",
"dependencies": {
"inflight": {
"version": "1.0.4",
"version": "1.0.5",
"dependencies": {
"wrappy": {
"version": "1.0.1"
"version": "1.0.2"
}
}
},
@@ -622,7 +634,7 @@
"version": "2.0.10",
"dependencies": {
"brace-expansion": {
"version": "1.1.4",
"version": "1.1.5",
"dependencies": {
"balanced-match": {
"version": "0.4.1"
@@ -638,7 +650,7 @@
"version": "1.3.3",
"dependencies": {
"wrappy": {
"version": "1.0.1"
"version": "1.0.2"
}
}
}
@@ -662,7 +674,7 @@
"version": "6.6.3",
"dependencies": {
"JSONStream": {
"version": "1.1.1",
"version": "1.1.3",
"dependencies": {
"jsonparse": {
"version": "1.2.0"
@@ -711,7 +723,7 @@
}
},
"process": {
"version": "0.11.3"
"version": "0.11.5"
},
"xtend": {
"version": "4.0.1"
@@ -741,7 +753,7 @@
"version": "3.9.1",
"dependencies": {
"JSONStream": {
"version": "1.1.1",
"version": "1.1.3",
"dependencies": {
"jsonparse": {
"version": "1.2.0"
@@ -884,7 +896,7 @@
"version": "1.4.2",
"dependencies": {
"process": {
"version": "0.11.3"
"version": "0.11.5"
}
}
},
@@ -3128,14 +3140,6 @@
},
"async-each": {
"version": "0.1.6"
},
"fsevents": {
"version": "0.3.8",
"dependencies": {
"nan": {
"version": "2.3.3"
}
}
}
}
}
@@ -6392,50 +6396,6 @@
}
}
},
"is-online": {
"version": "5.1.1",
"dependencies": {
"is-reachable": {
"version": "1.3.0",
"dependencies": {
"arrify": {
"version": "1.0.1"
},
"each-async": {
"version": "1.1.1",
"dependencies": {
"set-immediate-shim": {
"version": "1.0.1"
}
}
},
"is-port-reachable": {
"version": "1.0.0"
},
"router-ips": {
"version": "0.2.0"
},
"url-parse-lax": {
"version": "1.0.0",
"dependencies": {
"prepend-http": {
"version": "1.0.4"
}
}
}
}
},
"onetime": {
"version": "1.1.0"
},
"random-item": {
"version": "1.0.0"
},
"root-hints": {
"version": "1.0.10"
}
}
},
"jasmine-core": {
"version": "2.4.1"
},
@@ -6503,10 +6463,45 @@
}
},
"jasmine-reporters": {
"version": "1.0.2",
"version": "2.2.0",
"dependencies": {
"jasmine": {
"version": "2.4.1",
"dependencies": {
"exit": {
"version": "0.1.2"
},
"glob": {
"version": "3.2.11",
"dependencies": {
"inherits": {
"version": "2.0.1"
},
"minimatch": {
"version": "0.3.0",
"dependencies": {
"lru-cache": {
"version": "2.7.3"
},
"sigmund": {
"version": "1.0.1"
}
}
}
}
}
}
},
"mkdirp": {
"version": "0.3.5"
"version": "0.5.1",
"dependencies": {
"minimist": {
"version": "0.0.8"
}
}
},
"xmldom": {
"version": "0.1.22"
}
}
},
@@ -6963,398 +6958,6 @@
}
}
}
},
"fsevents": {
"version": "1.0.12",
"dependencies": {
"nan": {
"version": "2.3.3"
},
"node-pre-gyp": {
"version": "0.6.25",
"dependencies": {
"nopt": {
"version": "3.0.6",
"dependencies": {
"abbrev": {
"version": "1.0.7"
}
}
}
}
},
"ansi": {
"version": "0.3.1"
},
"ansi-regex": {
"version": "2.0.0"
},
"ansi-styles": {
"version": "2.2.1"
},
"are-we-there-yet": {
"version": "1.1.2"
},
"asn1": {
"version": "0.2.3"
},
"assert-plus": {
"version": "0.2.0"
},
"async": {
"version": "1.5.2"
},
"bl": {
"version": "1.0.3"
},
"aws-sign2": {
"version": "0.6.0"
},
"block-stream": {
"version": "0.0.8"
},
"boom": {
"version": "2.10.1"
},
"caseless": {
"version": "0.11.0"
},
"chalk": {
"version": "1.1.3"
},
"combined-stream": {
"version": "1.0.5"
},
"core-util-is": {
"version": "1.0.2"
},
"commander": {
"version": "2.9.0"
},
"deep-extend": {
"version": "0.4.1"
},
"cryptiles": {
"version": "2.0.5"
},
"ecc-jsbn": {
"version": "0.1.1"
},
"delayed-stream": {
"version": "1.0.0"
},
"debug": {
"version": "2.2.0"
},
"delegates": {
"version": "1.0.0"
},
"extend": {
"version": "3.0.0"
},
"escape-string-regexp": {
"version": "1.0.5"
},
"extsprintf": {
"version": "1.0.2"
},
"forever-agent": {
"version": "0.6.1"
},
"gauge": {
"version": "1.2.7"
},
"form-data": {
"version": "1.0.0-rc4"
},
"fstream": {
"version": "1.0.8"
},
"generate-object-property": {
"version": "1.2.0"
},
"generate-function": {
"version": "2.0.0"
},
"graceful-fs": {
"version": "4.1.3"
},
"graceful-readlink": {
"version": "1.0.1"
},
"har-validator": {
"version": "2.0.6"
},
"has-ansi": {
"version": "2.0.0"
},
"hawk": {
"version": "3.1.3"
},
"hoek": {
"version": "2.16.3"
},
"has-unicode": {
"version": "2.0.0"
},
"ini": {
"version": "1.3.4"
},
"http-signature": {
"version": "1.1.1"
},
"inherits": {
"version": "2.0.1"
},
"is-my-json-valid": {
"version": "2.13.1"
},
"is-property": {
"version": "1.0.2"
},
"isarray": {
"version": "1.0.0"
},
"is-typedarray": {
"version": "1.0.0"
},
"isstream": {
"version": "0.1.2"
},
"jodid25519": {
"version": "1.0.2"
},
"json-schema": {
"version": "0.2.2"
},
"jsbn": {
"version": "0.1.0"
},
"json-stringify-safe": {
"version": "5.0.1"
},
"jsonpointer": {
"version": "2.0.0"
},
"jsprim": {
"version": "1.2.2"
},
"lodash.pad": {
"version": "4.1.0"
},
"lodash.padend": {
"version": "4.2.0"
},
"lodash.padstart": {
"version": "4.2.0"
},
"lodash.repeat": {
"version": "4.0.0"
},
"lodash.tostring": {
"version": "4.1.2"
},
"mime-types": {
"version": "2.1.10"
},
"mime-db": {
"version": "1.22.0"
},
"minimist": {
"version": "0.0.8"
},
"mkdirp": {
"version": "0.5.1"
},
"ms": {
"version": "0.7.1"
},
"node-uuid": {
"version": "1.4.7"
},
"npmlog": {
"version": "2.0.3"
},
"oauth-sign": {
"version": "0.8.1"
},
"once": {
"version": "1.3.3"
},
"pinkie": {
"version": "2.0.4"
},
"pinkie-promise": {
"version": "2.0.0"
},
"qs": {
"version": "6.0.2"
},
"process-nextick-args": {
"version": "1.0.6"
},
"readable-stream": {
"version": "2.0.6"
},
"request": {
"version": "2.69.0"
},
"semver": {
"version": "5.1.0"
},
"sntp": {
"version": "1.0.9"
},
"sshpk": {
"version": "1.7.4"
},
"string_decoder": {
"version": "0.10.31"
},
"stringstream": {
"version": "0.0.5"
},
"strip-json-comments": {
"version": "1.0.4"
},
"strip-ansi": {
"version": "3.0.1"
},
"supports-color": {
"version": "2.0.0"
},
"tough-cookie": {
"version": "2.2.2"
},
"tar-pack": {
"version": "3.1.3"
},
"tunnel-agent": {
"version": "0.4.2"
},
"tar": {
"version": "2.2.1"
},
"tweetnacl": {
"version": "0.14.3"
},
"uid-number": {
"version": "0.0.6"
},
"util-deprecate": {
"version": "1.0.2"
},
"verror": {
"version": "1.3.6"
},
"wrappy": {
"version": "1.0.1"
},
"xtend": {
"version": "4.0.1"
},
"dashdash": {
"version": "1.13.0",
"dependencies": {
"assert-plus": {
"version": "1.0.0"
}
}
},
"rc": {
"version": "1.1.6",
"dependencies": {
"minimist": {
"version": "1.2.0"
}
}
},
"aws4": {
"version": "1.3.2",
"dependencies": {
"lru-cache": {
"version": "4.0.1",
"dependencies": {
"pseudomap": {
"version": "1.0.2"
},
"yallist": {
"version": "2.0.0"
}
}
}
}
},
"fstream-ignore": {
"version": "1.0.3",
"dependencies": {
"minimatch": {
"version": "3.0.0",
"dependencies": {
"brace-expansion": {
"version": "1.1.3",
"dependencies": {
"balanced-match": {
"version": "0.3.0"
},
"concat-map": {
"version": "0.0.1"
}
}
}
}
}
}
},
"rimraf": {
"version": "2.5.2",
"dependencies": {
"glob": {
"version": "7.0.3",
"dependencies": {
"inflight": {
"version": "1.0.4",
"dependencies": {
"wrappy": {
"version": "1.0.1"
}
}
},
"inherits": {
"version": "2.0.1"
},
"minimatch": {
"version": "3.0.0",
"dependencies": {
"brace-expansion": {
"version": "1.1.3",
"dependencies": {
"balanced-match": {
"version": "0.3.0"
},
"concat-map": {
"version": "0.0.1"
}
}
}
}
},
"once": {
"version": "1.3.3",
"dependencies": {
"wrappy": {
"version": "1.0.1"
}
}
},
"path-is-absolute": {
"version": "1.0.0"
}
}
}
}
}
}
}
}
},
@@ -8832,28 +8435,6 @@
},
"ultron": {
"version": "1.0.2"
},
"bufferutil": {
"version": "1.2.1",
"dependencies": {
"bindings": {
"version": "1.2.1"
},
"nan": {
"version": "2.3.3"
}
}
},
"utf-8-validate": {
"version": "1.2.1",
"dependencies": {
"bindings": {
"version": "1.2.1"
},
"nan": {
"version": "2.3.3"
}
}
}
}
},
+426 -1099
View File
File diff suppressed because it is too large Load Diff
+2 -3
View File
@@ -30,7 +30,7 @@
"commitizen": "^2.3.0",
"cz-conventional-changelog": "1.1.4",
"dgeni": "^0.4.0",
"dgeni-packages": "^0.13.0",
"dgeni-packages": "^0.14.0",
"event-stream": "~3.1.0",
"glob": "^6.0.1",
"grunt": "~0.4.2",
@@ -52,10 +52,9 @@
"gulp-sourcemaps": "^1.2.2",
"gulp-uglify": "^1.0.1",
"gulp-util": "^3.0.1",
"is-online": "^5.1.1",
"jasmine-core": "^2.4.0",
"jasmine-node": "^2.0.0",
"jasmine-reporters": "~1.0.1",
"jasmine-reporters": "^2.2.0",
"jshint": "^2.9.1",
"jshint-stylish": "^2.1.0",
"karma": "^0.13.19",
+2
View File
@@ -12,4 +12,6 @@ config.capabilities = {
browserName: 'chrome'
};
config.directConnect = true;
exports.config = config;
+5 -4
View File
@@ -15,7 +15,7 @@ exports.config = {
baseUrl: 'http://localhost:8000/',
framework: 'jasmine',
framework: 'jasmine2',
onPrepare: function() {
/* global angular: false, browser: false, jasmine: false */
@@ -29,9 +29,10 @@ exports.config = {
browser.addMockModule('disableNgAnimate', disableNgAnimate);
require('jasmine-reporters');
jasmine.getEnv().addReporter(
new jasmine.JUnitXmlReporter('test_out/docs-e2e-' + exports.config.capabilities.browserName + '-', true, true));
var reporters = require('jasmine-reporters');
jasmine.getEnv().addReporter(new reporters.JUnitXmlReporter({
savePath: 'test_out/docs-e2e-' + exports.config.capabilities.browserName + '-'
}));
},
jasmineNodeOpts: {
+1 -1
View File
@@ -5,7 +5,7 @@ exports.config = {
baseUrl: 'http://localhost:8000/',
framework: 'jasmine',
framework: 'jasmine2',
onPrepare: function() {
/* global angular: false, browser: false, jasmine: false */
+41 -34
View File
@@ -752,7 +752,13 @@ function arrayRemove(array, value) {
* * If a destination is provided, all of its elements (for arrays) or properties (for objects)
* are deleted and then all elements/properties from the source are copied to it.
* * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
* * If `source` is identical to 'destination' an exception will be thrown.
* * If `source` is identical to `destination` an exception will be thrown.
*
* <br />
* <div class="alert alert-warning">
* Only enumerable properties are taken into account. Non-enumerable properties (both on `source`
* and on `destination`) will be ignored.
* </div>
*
* @param {*} source The source that will be used to make a copy.
* Can be any type, including primitives, `null`, and `undefined`.
@@ -761,41 +767,42 @@ function arrayRemove(array, value) {
* @returns {*} The copy or updated `destination`, if `destination` was specified.
*
* @example
<example module="copyExample">
<file name="index.html">
<div ng-controller="ExampleController">
<form novalidate class="simple-form">
Name: <input type="text" ng-model="user.name" /><br />
E-mail: <input type="email" ng-model="user.email" /><br />
Gender: <input type="radio" ng-model="user.gender" value="male" />male
<input type="radio" ng-model="user.gender" value="female" />female<br />
<button ng-click="reset()">RESET</button>
<button ng-click="update(user)">SAVE</button>
</form>
<pre>form = {{user | json}}</pre>
<pre>master = {{master | json}}</pre>
</div>
<example module="copyExample">
<file name="index.html">
<div ng-controller="ExampleController">
<form novalidate class="simple-form">
<label>Name: <input type="text" ng-model="user.name" /></label><br />
<label>Age: <input type="number" ng-model="user.age" /></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 />
<button ng-click="reset()">RESET</button>
<button ng-click="update(user)">SAVE</button>
</form>
<pre>form = {{user | json}}</pre>
<pre>master = {{master | json}}</pre>
</div>
</file>
<file name="script.js">
// Module: copyExample
angular.
module('copyExample', []).
controller('ExampleController', ['$scope', function($scope) {
$scope.master = {};
<script>
angular.module('copyExample', [])
.controller('ExampleController', ['$scope', function($scope) {
$scope.master= {};
$scope.reset = function() {
// Example with 1 argument
$scope.user = angular.copy($scope.master);
};
$scope.update = function(user) {
// Example with 1 argument
$scope.master= angular.copy(user);
};
$scope.update = function(user) {
// Example with 2 arguments
angular.copy(user, $scope.master);
};
$scope.reset = function() {
// Example with 2 arguments
angular.copy($scope.master, $scope.user);
};
$scope.reset();
}]);
</script>
</file>
</example>
$scope.reset();
}]);
</file>
</example>
*/
function copy(source, destination) {
var stackSource = [];
@@ -902,7 +909,7 @@ function copy(source, destination) {
case '[object Uint8ClampedArray]':
case '[object Uint16Array]':
case '[object Uint32Array]':
return new source.constructor(copyElement(source.buffer));
return new source.constructor(copyElement(source.buffer), source.byteOffset, source.length);
case '[object ArrayBuffer]':
//Support: IE10
+3 -1
View File
@@ -76,6 +76,7 @@
$HttpParamSerializerJQLikeProvider,
$HttpBackendProvider,
$xhrFactoryProvider,
$jsonpCallbacksProvider,
$LocationProvider,
$LogProvider,
$ParseProvider,
@@ -148,7 +149,7 @@ function publishExternalAPI(angular) {
'isDate': isDate,
'lowercase': lowercase,
'uppercase': uppercase,
'callbacks': {counter: 0},
'callbacks': {$$counter: 0},
'getTestability': getTestability,
'$$minErr': minErr,
'$$csp': csp,
@@ -237,6 +238,7 @@ function publishExternalAPI(angular) {
$httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
$httpBackend: $HttpBackendProvider,
$xhrFactory: $xhrFactoryProvider,
$jsonpCallbacks: $jsonpCallbacksProvider,
$location: $LocationProvider,
$log: $LogProvider,
$parse: $ParseProvider,
+3 -3
View File
@@ -853,10 +853,10 @@ function createInjector(modulesToLoad, strictDi) {
if (msie <= 11) {
return false;
}
// Workaround for MS Edge.
// Check https://connect.microsoft.com/IE/Feedback/Details/2211653
// Support: Edge 12-13 only
// See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/
return typeof func === 'function'
&& /^(?:class\s|constructor\()/.test(stringifyFn(func));
&& /^(?:class\b|constructor\()/.test(stringifyFn(func));
}
function invoke(fn, self, locals, serviceName) {
+1 -1
View File
@@ -215,7 +215,7 @@ function jqLiteBuildFragment(html, context) {
nodes.push(context.createTextNode(html));
} else {
// Convert html into DOM nodes
tmp = tmp || fragment.appendChild(context.createElement("div"));
tmp = fragment.appendChild(context.createElement("div"));
tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
wrap = wrapMap[tag] || wrapMap._default;
tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
+148 -37
View File
@@ -52,8 +52,9 @@
* There are many different options for a directive.
*
* The difference resides in the return value of the factory function.
* You can either return a "Directive Definition Object" (see below) that defines the directive properties,
* or just the `postLink` function (all other properties will have the default values).
* You can either return a {@link $compile#directive-definition-object Directive Definition Object (see below)}
* that defines the directive properties, or just the `postLink` function (all other properties will have
* the default values).
*
* <div class="alert alert-success">
* **Best Practice:** It's recommended to use the "directive definition object" form.
@@ -117,6 +118,125 @@
* });
* ```
*
* ### Life-cycle hooks
* Directive controllers can provide the following methods that are called by Angular at points in the life-cycle of the
* directive:
* * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
* had their bindings initialized (and before the pre &amp; post linking functions for the directives on
* this element). This is a good place to put initialization code for your controller.
* * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
* `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
* object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
* component such as cloning the bound value to prevent accidental mutation of the outer value.
* * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on
* changes. Any actions that you wish to take in response to the changes that you detect must be
* invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook
* could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not
* be detected by Angular's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments;
* if detecting changes, you must store the previous value(s) for comparison to the current values.
* * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
* external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
* the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
* components will have their `$onDestroy()` hook called before child components.
* * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
* function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
* Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
* they are waiting for their template to load asynchronously and their own compilation and linking has been
* suspended until that occurs.
*
* #### Comparison with Angular 2 life-cycle hooks
* Angular 2 also uses life-cycle hooks for its components. While the Angular 1 life-cycle hooks are similar there are
* some differences that you should be aware of, especially when it comes to moving your code from Angular 1 to Angular 2:
*
* * Angular 1 hooks are prefixed with `$`, such as `$onInit`. Angular 2 hooks are prefixed with `ng`, such as `ngOnInit`.
* * Angular 1 hooks can be defined on the controller prototype or added to the controller inside its constructor.
* In Angular 2 you can only define hooks on the prototype of the Component class.
* * Due to the differences in change-detection, you may get many more calls to `$doCheck` in Angular 1 than you would to
* `ngDoCheck` in Angular 2
* * Changes to the model inside `$doCheck` will trigger new turns of the digest loop, which will cause the changes to be
* propagated throughout the application.
* Angular 2 does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an
* error or do nothing depending upon the state of `enableProdMode()`.
*
* #### Life-cycle hook examples
*
* This example shows how you can check for mutations to a Date object even though the identity of the object
* has not changed.
*
* <example name="doCheckDateExample" module="do-check-module">
* <file name="app.js">
* angular.module('do-check-module', [])
* .component('app', {
* template:
* 'Month: <input ng-model="$ctrl.month" ng-change="$ctrl.updateDate()">' +
* 'Date: {{ $ctrl.date }}' +
* '<test date="$ctrl.date"></test>',
* controller: function() {
* this.date = new Date();
* this.month = this.date.getMonth();
* this.updateDate = function() {
* this.date.setMonth(this.month);
* };
* }
* })
* .component('test', {
* bindings: { date: '<' },
* template:
* '<pre>{{ $ctrl.log | json }}</pre>',
* controller: function() {
* var previousValue;
* this.log = [];
* this.$doCheck = function() {
* var currentValue = this.date && this.date.valueOf();
* if (previousValue !== currentValue) {
* this.log.push('doCheck: date mutated: ' + this.date);
* previousValue = currentValue;
* }
* };
* }
* });
* </file>
* <file name="index.html">
* <app></app>
* </file>
* </example>
*
* This example show how you might use `$doCheck` to trigger changes in your component's inputs even if the
* actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large
* arrays or objects can have a negative impact on your application performance)
*
* <example name="doCheckArrayExample" module="do-check-module">
* <file name="index.html">
* <div ng-init="items = []">
* <button ng-click="items.push(items.length)">Add Item</button>
* <button ng-click="items = []">Reset Items</button>
* <pre>{{ items }}</pre>
* <test items="items"></test>
* </div>
* </file>
* <file name="app.js">
* angular.module('do-check-module', [])
* .component('test', {
* bindings: { items: '<' },
* template:
* '<pre>{{ $ctrl.log | json }}</pre>',
* controller: function() {
* this.log = [];
*
* this.$doCheck = function() {
* if (this.items_ref !== this.items) {
* this.log.push('doCheck: items changed');
* this.items_ref = this.items;
* }
* if (!angular.equals(this.items_clone, this.items)) {
* this.log.push('doCheck: items mutated');
* this.items_clone = angular.copy(this.items);
* }
* };
* }
* });
* </file>
* </example>
*
*
* ### Directive Definition Object
@@ -292,25 +412,6 @@
* The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
* `true` if the specified slot contains content (i.e. one or more DOM nodes).
*
* The controller can provide the following methods that act as life-cycle hooks:
* * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
* had their bindings initialized (and before the pre &amp; post linking functions for the directives on
* this element). This is a good place to put initialization code for your controller.
* * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
* `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
* object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
* component such as cloning the bound value to prevent accidental mutation of the outer value.
* * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
* external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
* the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
* components will have their `$onDestroy()` hook called before child components.
* * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
* function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
* Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
* they are waiting for their template to load asynchronously and their own compilation and linking has been
* suspended until that occurs.
*
*
* #### `require`
* Require another directive and inject its controller as the fourth argument to the linking function. The
* `require` property can be a string, an array or an object:
@@ -508,8 +609,8 @@
* any other controller.
*
* * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
* This is the same as the `$transclude`
* parameter of directive controllers, see there for details.
* This is the same as the `$transclude` parameter of directive controllers,
* see {@link ng.$compile#-controller- the controller section for details}.
* `function([scope], cloneLinkingFn, futureParentElement)`.
*
* #### Pre-linking function
@@ -1964,19 +2065,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
addTextInterpolateDirective(directives, node.nodeValue);
break;
case NODE_TYPE_COMMENT: /* Comment */
try {
match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
if (match) {
nName = directiveNormalize(match[1]);
if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
attrs[nName] = trim(match[2]);
}
}
} catch (e) {
// turns out that under some circumstances IE9 throws errors when one attempts to read
// comment's node value.
// Just ignore it and continue. (Can't seem to reproduce in test case.)
}
collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective);
break;
}
@@ -1984,6 +2073,24 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return directives;
}
function collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
// function created because of performance, try/catch disables
// the optimization of the whole function #14848
try {
var match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
if (match) {
var nName = directiveNormalize(match[1]);
if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
attrs[nName] = trim(match[2]);
}
}
} catch (e) {
// turns out that under some circumstances IE9 throws errors when one attempts to read
// comment's node value.
// Just ignore it and continue. (Can't seem to reproduce in test case.)
}
}
/**
* Given a node with an directive-start it collects all of the siblings until it finds
* directive-end.
@@ -2512,6 +2619,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
$exceptionHandler(e);
}
}
if (isFunction(controllerInstance.$doCheck)) {
controllerScope.$watch(function() { controllerInstance.$doCheck(); });
controllerInstance.$doCheck();
}
if (isFunction(controllerInstance.$onDestroy)) {
controllerScope.$on('$destroy', function callOnDestroyHook() {
controllerInstance.$onDestroy();
@@ -3158,7 +3269,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
forEach(bindings, function initializeBinding(definition, scopeName) {
var attrName = definition.attrName,
optional = definition.optional,
mode = definition.mode, // @, =, or &
mode = definition.mode, // @, =, <, or &
lastValue,
parentGet, parentSet, compare, removeWatch;
+16 -2
View File
@@ -230,9 +230,11 @@
*
* @description
*
* Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
* Sets the `readonly` attribute on the element, if the expression inside `ngReadonly` is truthy.
* Note that `readonly` applies only to `input` elements with specific types. [See the input docs on
* MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly) for more information.
*
* A special directive is necessary because we cannot use interpolation inside the `readOnly`
* A special directive is necessary because we cannot use interpolation inside the `readonly`
* attribute. See the {@link guide/interpolation interpolation guide} for more info.
*
* @example
@@ -269,6 +271,13 @@
* A special directive is necessary because we cannot use interpolation inside the `selected`
* attribute. See the {@link guide/interpolation interpolation guide} for more info.
*
* <div class="alert alert-warning">
* **Note:** `ngSelected` does not interact with the `select` and `ngModel` directives, it only
* sets the `selected` attribute on the element. If you are using `ngModel` on the select, you
* should not use `ngSelected` on the options, as `ngModel` will set the select value and
* selected options.
* </div>
*
* @example
<example>
<file name="index.html">
@@ -305,6 +314,11 @@
* A special directive is necessary because we cannot use interpolation inside the `open`
* attribute. See the {@link guide/interpolation interpolation guide} for more info.
*
* ## A note about browser compatibility
*
* Edge, Firefox, and Internet Explorer do not support the `details` element, it is
* recommended to use {@link ng.ngShow} and {@link ng.ngHide} instead.
*
* @example
<example>
<file name="index.html">
+2 -2
View File
@@ -1411,7 +1411,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
attr.$observe('min', function(val) {
if (isDefined(val) && !isNumber(val)) {
val = parseFloat(val, 10);
val = parseFloat(val);
}
minVal = isNumber(val) && !isNaN(val) ? val : undefined;
// TODO(matsko): implement validateLater to reduce number of validations
@@ -1427,7 +1427,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
attr.$observe('max', function(val) {
if (isDefined(val) && !isNumber(val)) {
val = parseFloat(val, 10);
val = parseFloat(val);
}
maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
// TODO(matsko): implement validateLater to reduce number of validations
+5
View File
@@ -153,6 +153,11 @@ function classDirective(name, selector) {
* When the expression changes, the previously added classes are removed and only then are the
* new classes added.
*
* @knownIssue
* You should not use {@link guide/interpolation interpolation} in the value of the `class`
* attribute, when using the `ngClass` directive on the same element.
* See {@link guide/interpolation#known-issues here} for more info.
*
* @animations
* | Animation | Occurs |
* |----------------------------------|-------------------------------------|
+3 -2
View File
@@ -629,7 +629,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
for (var i = options.items.length - 1; i >= 0; i--) {
var option = options.items[i];
if (option.group) {
if (isDefined(option.group)) {
jqLiteRemove(option.element.parentNode);
} else {
jqLiteRemove(option.element);
@@ -661,7 +661,8 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
listFragment.appendChild(groupElement);
// Update the label on the group element
groupElement.label = option.group;
// "null" is special cased because of Safari
groupElement.label = option.group === null ? 'null' : option.group;
// Store it for use later
groupElementMap[option.group] = groupElement;
+5
View File
@@ -8,6 +8,11 @@
* @description
* The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
*
* @knownIssue
* You should not use {@link guide/interpolation interpolation} in the value of the `style`
* attribute, when using the `ngStyle` directive on the same element.
* See {@link guide/interpolation#known-issues here} for more info.
*
* @element ANY
* @param {expression} ngStyle
*
+55 -30
View File
@@ -159,35 +159,60 @@
* </example>
*/
var ngTranscludeMinErr = minErr('ngTransclude');
var ngTranscludeDirective = ngDirective({
restrict: 'EAC',
link: function($scope, $element, $attrs, controller, $transclude) {
var ngTranscludeDirective = ['$compile', function($compile) {
return {
restrict: 'EAC',
terminal: true,
compile: function ngTranscludeCompile(tElement) {
if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
// If the attribute is of the form: `ng-transclude="ng-transclude"`
// then treat it like the default
$attrs.ngTransclude = '';
// Remove and cache any original content to act as a fallback
var fallbackLinkFn = $compile(tElement.contents());
tElement.empty();
return function ngTranscludePostLink($scope, $element, $attrs, controller, $transclude) {
if (!$transclude) {
throw ngTranscludeMinErr('orphan',
'Illegal use of ngTransclude directive in the template! ' +
'No parent directive that requires a transclusion found. ' +
'Element: {0}',
startingTag($element));
}
// If the attribute is of the form: `ng-transclude="ng-transclude"` then treat it like the default
if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
$attrs.ngTransclude = '';
}
var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
// If the slot is required and no transclusion content is provided then this call will throw an error
$transclude(ngTranscludeCloneAttachFn, null, slotName);
// If the slot is optional and no transclusion content is provided then use the fallback content
if (slotName && !$transclude.isSlotFilled(slotName)) {
useFallbackContent();
}
function ngTranscludeCloneAttachFn(clone, transcludedScope) {
if (clone.length) {
$element.append(clone);
} else {
useFallbackContent();
// There is nothing linked against the transcluded scope since no content was available,
// so it should be safe to clean up the generated scope.
transcludedScope.$destroy();
}
}
function useFallbackContent() {
// Since this is the fallback content rather than the transcluded content,
// we link against the scope of this directive rather than the transcluded scope
fallbackLinkFn($scope, function(clone) {
$element.append(clone);
});
}
};
}
function ngTranscludeCloneAttachFn(clone) {
if (clone.length) {
$element.empty();
$element.append(clone);
}
}
if (!$transclude) {
throw ngTranscludeMinErr('orphan',
'Illegal use of ngTransclude directive in the template! ' +
'No parent directive that requires a transclusion found. ' +
'Element: {0}',
startingTag($element));
}
// If there is no slot name defined or the slot name is not optional
// then transclude the slot
var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
$transclude(ngTranscludeCloneAttachFn, null, slotName);
}
});
};
}];
+1 -1
View File
@@ -27,7 +27,7 @@
* logErrorsToBackend(exception, cause);
* $log.warn(exception, cause);
* };
* });
* }]);
* ```
*
* <hr />
+23 -17
View File
@@ -22,10 +22,11 @@
* - `Object`: A pattern object can be used to filter specific properties on objects contained
* by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
* which have property `name` containing "M" and property `phone` containing "1". A special
* property name `$` can be used (as in `{$:"text"}`) to accept a match against any
* property of the object or its nested object properties. That's equivalent to the simple
* substring match with a `string` as described above. The predicate can be negated by prefixing
* the string with `!`.
* property name (`$` by default) can be used (e.g. as in `{$: "text"}`) to accept a match
* against any property of the object or its nested object properties. That's equivalent to the
* simple substring match with a `string` as described above. The special property name can be
* overwritten, using the `anyPropertyKey` parameter.
* The predicate can be negated by prefixing the string with `!`.
* For example `{name: "!M"}` predicate will return an array of items which have property `name`
* not containing "M".
*
@@ -59,6 +60,9 @@
* Primitive values are converted to strings. Objects are not compared against primitives,
* unless they have a custom `toString` method (e.g. `Date` objects).
*
* @param {string=} anyPropertyKey The special property name that matches against any property.
* By default `$`.
*
* @example
<example>
<file name="index.html">
@@ -127,8 +131,9 @@
</file>
</example>
*/
function filterFilter() {
return function(array, expression, comparator) {
return function(array, expression, comparator, anyPropertyKey) {
if (!isArrayLike(array)) {
if (array == null) {
return array;
@@ -137,6 +142,7 @@ function filterFilter() {
}
}
anyPropertyKey = anyPropertyKey || '$';
var expressionType = getTypeForFilter(expression);
var predicateFn;
var matchAgainstAnyProp;
@@ -153,7 +159,7 @@ function filterFilter() {
//jshint -W086
case 'object':
//jshint +W086
predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
predicateFn = createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp);
break;
default:
return array;
@@ -164,8 +170,8 @@ function filterFilter() {
}
// Helper functions for `filterFilter`
function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
function createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp) {
var shouldMatchPrimitives = isObject(expression) && (anyPropertyKey in expression);
var predicateFn;
if (comparator === true) {
@@ -193,25 +199,25 @@ function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
predicateFn = function(item) {
if (shouldMatchPrimitives && !isObject(item)) {
return deepCompare(item, expression.$, comparator, false);
return deepCompare(item, expression[anyPropertyKey], comparator, anyPropertyKey, false);
}
return deepCompare(item, expression, comparator, matchAgainstAnyProp);
return deepCompare(item, expression, comparator, anyPropertyKey, matchAgainstAnyProp);
};
return predicateFn;
}
function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstAnyProp, dontMatchWholeObject) {
var actualType = getTypeForFilter(actual);
var expectedType = getTypeForFilter(expected);
if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
return !deepCompare(actual, expected.substring(1), comparator, anyPropertyKey, matchAgainstAnyProp);
} else if (isArray(actual)) {
// In case `actual` is an array, consider it a match
// if ANY of it's items matches `expected`
return actual.some(function(item) {
return deepCompare(item, expected, comparator, matchAgainstAnyProp);
return deepCompare(item, expected, comparator, anyPropertyKey, matchAgainstAnyProp);
});
}
@@ -220,11 +226,11 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatc
var key;
if (matchAgainstAnyProp) {
for (key in actual) {
if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
return true;
}
}
return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, anyPropertyKey, false);
} else if (expectedType === 'object') {
for (key in expected) {
var expectedVal = expected[key];
@@ -232,9 +238,9 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatc
continue;
}
var matchAnyProperty = key === '$';
var matchAnyProperty = key === anyPropertyKey;
var actualVal = matchAnyProperty ? actual : actual[key];
if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
if (!deepCompare(actualVal, expectedVal, comparator, anyPropertyKey, matchAnyProperty, matchAnyProperty)) {
return false;
}
}
+56 -42
View File
@@ -35,7 +35,7 @@ function $HttpParamSerializerProvider() {
* * `{'foo': 'bar'}` results in `foo=bar`
* * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
* * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
* * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
* * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D` (stringified and encoded representation of an object)
*
* Note that serializer will sort the request parameters alphabetically.
* */
@@ -586,7 +586,7 @@ function $HttpProvider() {
*
* ### Overriding the Default Transformations Per Request
*
* If you wish override the request/response transformations only for a single request then provide
* If you wish to override the request/response transformations only for a single request then provide
* `transformRequest` and/or `transformResponse` properties on the configuration object passed
* into `$http`.
*
@@ -629,7 +629,7 @@ function $HttpProvider() {
* * cache a specific response - set config.cache value to TRUE or to a cache object
*
* If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
* then the default `$cacheFactory($http)` object is used.
* then the default `$cacheFactory("$http")` object is used.
*
* The default cache value can be set by updating the
* {@link ng.$http#defaults `$http.defaults.cache`} property or the
@@ -957,48 +957,25 @@ function $HttpProvider() {
config.headers = mergeHeaders(requestConfig);
config.method = uppercase(config.method);
config.paramSerializer = isString(config.paramSerializer) ?
$injector.get(config.paramSerializer) : config.paramSerializer;
$injector.get(config.paramSerializer) : config.paramSerializer;
var serverRequest = function(config) {
var headers = config.headers;
var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
// strip content-type if data is undefined
if (isUndefined(reqData)) {
forEach(headers, function(value, header) {
if (lowercase(header) === 'content-type') {
delete headers[header];
}
});
}
if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
config.withCredentials = defaults.withCredentials;
}
// send request
return sendReq(config, reqData).then(transformResponse, transformResponse);
};
var chain = [serverRequest, undefined];
var requestInterceptors = [];
var responseInterceptors = [];
var promise = $q.when(config);
// apply interceptors
forEach(reversedInterceptors, function(interceptor) {
if (interceptor.request || interceptor.requestError) {
chain.unshift(interceptor.request, interceptor.requestError);
requestInterceptors.unshift(interceptor.request, interceptor.requestError);
}
if (interceptor.response || interceptor.responseError) {
chain.push(interceptor.response, interceptor.responseError);
responseInterceptors.push(interceptor.response, interceptor.responseError);
}
});
while (chain.length) {
var thenFn = chain.shift();
var rejectFn = chain.shift();
promise = promise.then(thenFn, rejectFn);
}
promise = chainInterceptors(promise, requestInterceptors);
promise = promise.then(serverRequest);
promise = chainInterceptors(promise, responseInterceptors);
if (useLegacyPromise) {
promise.success = function(fn) {
@@ -1025,14 +1002,18 @@ function $HttpProvider() {
return promise;
function transformResponse(response) {
// make a copy since the response must be cacheable
var resp = extend({}, response);
resp.data = transformData(response.data, response.headers, response.status,
config.transformResponse);
return (isSuccess(response.status))
? resp
: $q.reject(resp);
function chainInterceptors(promise, interceptors) {
for (var i = 0, ii = interceptors.length; i < ii;) {
var thenFn = interceptors[i++];
var rejectFn = interceptors[i++];
promise = promise.then(thenFn, rejectFn);
}
interceptors.length = 0;
return promise;
}
function executeHeaderFns(headers, config) {
@@ -1076,6 +1057,37 @@ function $HttpProvider() {
// execute if header value is a function for merged headers
return executeHeaderFns(reqHeaders, shallowCopy(config));
}
function serverRequest(config) {
var headers = config.headers;
var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
// strip content-type if data is undefined
if (isUndefined(reqData)) {
forEach(headers, function(value, header) {
if (lowercase(header) === 'content-type') {
delete headers[header];
}
});
}
if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
config.withCredentials = defaults.withCredentials;
}
// send request
return sendReq(config, reqData).then(transformResponse, transformResponse);
}
function transformResponse(response) {
// make a copy since the response must be cacheable
var resp = extend({}, response);
resp.data = transformData(response.data, response.headers, response.status,
config.transformResponse);
return (isSuccess(response.status))
? resp
: $q.reject(resp);
}
}
$http.pendingRequests = [];
@@ -1122,6 +1134,8 @@ function $HttpProvider() {
*
* @description
* Shortcut method to perform `JSONP` request.
* If you would like to customise where and how the callbacks are stored then try overriding
* or decorating the {@link $jsonpCallbacks} service.
*
* @param {string} url Relative or absolute URL specifying the destination of the request.
* The name of the callback should be the string `JSON_CALLBACK`.
+13 -16
View File
@@ -32,7 +32,7 @@ function $xhrFactoryProvider() {
/**
* @ngdoc service
* @name $httpBackend
* @requires $window
* @requires $jsonpCallbacks
* @requires $document
* @requires $xhrFactory
*
@@ -47,8 +47,8 @@ function $xhrFactoryProvider() {
* $httpBackend} which can be trained with responses.
*/
function $HttpBackendProvider() {
this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
this.$get = ['$browser', '$jsonpCallbacks', '$document', '$xhrFactory', function($browser, $jsonpCallbacks, $document, $xhrFactory) {
return createHttpBackend($browser, $xhrFactory, $browser.defer, $jsonpCallbacks, $document[0]);
}];
}
@@ -58,17 +58,13 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
$browser.$$incOutstandingRequestCount();
url = url || $browser.url();
if (lowercase(method) == 'jsonp') {
var callbackId = '_' + (callbacks.counter++).toString(36);
callbacks[callbackId] = function(data) {
callbacks[callbackId].data = data;
callbacks[callbackId].called = true;
};
var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
callbackId, function(status, text) {
completeRequest(callback, status, callbacks[callbackId].data, "", text);
callbacks[callbackId] = noop;
if (lowercase(method) === 'jsonp') {
var callbackPath = callbacks.createCallback(url);
var jsonpDone = jsonpReq(url, callbackPath, function(status, text) {
// jsonpReq only ever sets status to 200 (OK), 404 (ERROR) or -1 (WAITING)
var response = (status === 200) && callbacks.getResponse(callbackPath);
completeRequest(callback, status, response, "", text);
callbacks.removeCallback(callbackPath);
});
} else {
@@ -170,7 +166,8 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
}
};
function jsonpReq(url, callbackId, done) {
function jsonpReq(url, callbackPath, done) {
url = url.replace('JSON_CALLBACK', callbackPath);
// we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
// - fetches local scripts via XHR and evals them
// - adds and immediately removes script elements from the document
@@ -188,7 +185,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
var text = "unknown";
if (event) {
if (event.type === "load" && !callbacks[callbackId].called) {
if (event.type === "load" && !callbacks.wasCalled(callbackPath)) {
event = { type: "error" };
}
text = event.type;
+1 -1
View File
@@ -180,7 +180,7 @@ function $InterpolateProvider() {
*
* `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
*
* ####Escaped Interpolation
* #### Escaped Interpolation
* $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
* can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
* It will be rendered as a regular start/end marker, and will not be interpreted as an expression
+82
View File
@@ -0,0 +1,82 @@
'use strict';
/**
* @ngdoc service
* @name $jsonpCallbacks
* @requires $window
* @description
* This service handles the lifecycle of callbacks to handle JSONP requests.
* Override this service if you wish to customise where the callbacks are stored and
* how they vary compared to the requested url.
*/
var $jsonpCallbacksProvider = function() {
this.$get = ['$window', function($window) {
var callbacks = $window.angular.callbacks;
var callbackMap = {};
function createCallback(callbackId) {
var callback = function(data) {
callback.data = data;
callback.called = true;
};
callback.id = callbackId;
return callback;
}
return {
/**
* @ngdoc method
* @name $jsonpCallbacks#createCallback
* @param {string} url the url of the JSONP request
* @returns {string} the callback path to send to the server as part of the JSONP request
* @description
* {@link $httpBackend} calls this method to create a callback and get hold of the path to the callback
* to pass to the server, which will be used to call the callback with its payload in the JSONP response.
*/
createCallback: function(url) {
var callbackId = '_' + (callbacks.$$counter++).toString(36);
var callbackPath = 'angular.callbacks.' + callbackId;
var callback = createCallback(callbackId);
callbackMap[callbackPath] = callbacks[callbackId] = callback;
return callbackPath;
},
/**
* @ngdoc method
* @name $jsonpCallbacks#wasCalled
* @param {string} callbackPath the path to the callback that was sent in the JSONP request
* @returns {boolean} whether the callback has been called, as a result of the JSONP response
* @description
* {@link $httpBackend} calls this method to find out whether the JSONP response actually called the
* callback that was passed in the request.
*/
wasCalled: function(callbackPath) {
return callbackMap[callbackPath].called;
},
/**
* @ngdoc method
* @name $jsonpCallbacks#getResponse
* @param {string} callbackPath the path to the callback that was sent in the JSONP request
* @returns {*} the data received from the response via the registered callback
* @description
* {@link $httpBackend} calls this method to get hold of the data that was provided to the callback
* in the JSONP response.
*/
getResponse: function(callbackPath) {
return callbackMap[callbackPath].data;
},
/**
* @ngdoc method
* @name $jsonpCallbacks#removeCallback
* @param {string} callbackPath the path to the callback that was sent in the JSONP request
* @description
* {@link $httpBackend} calls this method to remove the callback after the JSONP request has
* completed or timed-out.
*/
removeCallback: function(callbackPath) {
var callback = callbackMap[callbackPath];
delete callbacks[callback.id];
delete callbackMap[callbackPath];
}
};
}];
};
+28 -2
View File
@@ -135,7 +135,7 @@
*
* **Methods**
*
* - `then(successCallback, errorCallback, notifyCallback)` regardless of when the promise was or
* - `then(successCallback, [errorCallback], [notifyCallback])` regardless of when the promise was or
* will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
* as soon as the result is available. The callbacks are called with a single argument: the result
* or rejection reason. Additionally, the notify callback may be called zero or more times to
@@ -146,7 +146,8 @@
* with the value which is resolved in that promise using
* [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
* It also notifies via the return value of the `notifyCallback` method. The promise cannot be
* resolved or rejected from the notifyCallback method.
* resolved or rejected from the notifyCallback method. The errorCallback and notifyCallback
* arguments are optional.
*
* - `catch(errorCallback)` shorthand for `promise.then(null, errorCallback)`
*
@@ -561,6 +562,30 @@ function qFactory(nextTick, exceptionHandler) {
return deferred.promise;
}
/**
* @ngdoc method
* @name $q#race
* @kind function
*
* @description
* Returns a promise that resolves or rejects as soon as one of those promises
* resolves or rejects, with the value or reason from that promise.
*
* @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
* @returns {Promise} a promise that resolves or rejects as soon as one of the `promises`
* resolves or rejects, with the value or reason from that promise.
*/
function race(promises) {
var deferred = defer();
forEach(promises, function(promise) {
when(promise).then(deferred.resolve, deferred.reject);
});
return deferred.promise;
}
var $Q = function Q(resolver) {
if (!isFunction(resolver)) {
throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
@@ -590,6 +615,7 @@ function qFactory(nextTick, exceptionHandler) {
$Q.when = when;
$Q.resolve = resolve;
$Q.all = all;
$Q.race = race;
return $Q;
}
+67 -55
View File
@@ -6,69 +6,81 @@
"window": false,
"angular": false,
"noop": false,
"copy": false,
"forEach": false,
"extend": false,
"jqLite": false,
"forEach": false,
"isArray": false,
"isString": false,
"isObject": false,
"isUndefined": false,
"isDefined": false,
"isFunction": false,
"isElement": false,
/* ng helpers */
"copy": true,
"extend": true,
"forEach": true,
"isArray": true,
"isDefined": true,
"isElement": true,
"isFunction": true,
"isObject": true,
"isString": true,
"isUndefined": true,
"jqLite": true,
"noop": true,
"ELEMENT_NODE": false,
"COMMENT_NODE": false,
"NG_ANIMATE_CLASSNAME": false,
"NG_ANIMATE_CHILDREN_DATA": false,
/* ngAnimate constants */
"COMMENT_NODE": true,
"ELEMENT_NODE": true,
"NG_ANIMATE_CLASSNAME": true,
"NG_ANIMATE_CHILDREN_DATA": true,
"ADD_CLASS_SUFFIX": false,
"REMOVE_CLASS_SUFFIX": false,
"EVENT_CLASS_PREFIX": false,
"ACTIVE_CLASS_SUFFIX": false,
"PREPARE_CLASS_SUFFIX": false,
/* ngAnimate className constants */
"ADD_CLASS_SUFFIX": true,
"REMOVE_CLASS_SUFFIX": true,
"EVENT_CLASS_PREFIX": true,
"ACTIVE_CLASS_SUFFIX": true,
"PREPARE_CLASS_SUFFIX": true,
"TRANSITION_DURATION_PROP": false,
"TRANSITION_DELAY_PROP": false,
"TRANSITION_PROP": false,
"PROPERTY_KEY": false,
"DURATION_KEY": false,
"DELAY_KEY": false,
"TIMING_KEY": false,
"ANIMATION_DURATION_PROP": false,
"ANIMATION_DELAY_PROP": false,
"ANIMATION_PROP": false,
"ANIMATION_ITERATION_COUNT_KEY": false,
"SAFE_FAST_FORWARD_DURATION_VALUE": false,
"TRANSITIONEND_EVENT": false,
"ANIMATIONEND_EVENT": false,
/* ngAnimate CSS constants */
"ANIMATION_DELAY_PROP": true,
"ANIMATION_DURATION_PROP": true,
"ANIMATION_ITERATION_COUNT_KEY": true,
"ANIMATION_PROP": true,
"ANIMATIONEND_EVENT": true,
"DELAY_KEY": true,
"DURATION_KEY": true,
"PROPERTY_KEY": true,
"SAFE_FAST_FORWARD_DURATION_VALUE": true,
"TIMING_KEY": true,
"TRANSITION_DELAY_PROP": true,
"TRANSITION_DURATION_PROP": true,
"TRANSITION_PROP": true,
"TRANSITIONEND_EVENT": true,
"assertArg": false,
"isPromiseLike": false,
"mergeClasses": false,
"mergeAnimationDetails": false,
"prepareAnimationOptions": false,
"applyAnimationStyles": false,
"applyAnimationFromStyles": false,
"applyAnimationToStyles": false,
/* ngAnimate helpers */
"applyAnimationClassesFactory": false,
"pendClasses": false,
"normalizeCssProp": false,
"packageStyles": false,
"removeFromArray": false,
"stripCommentsFromElement": false,
"applyAnimationFromStyles": false,
"applyAnimationStyles": false,
"applyAnimationToStyles": false,
"applyGeneratedPreparationClasses": false,
"applyInlineStyle": false,
"assertArg": false,
"blockKeyframeAnimations": false,
"blockTransitions": false,
"clearGeneratedClasses": false,
"concatWithSpace": false,
"extractElementNode": false,
"getDomNode": false,
"mergeAnimationDetails": false,
"mergeClasses": false,
"packageStyles": false,
"pendClasses": false,
"prepareAnimationOptions": false,
"removeFromArray": false,
"stripCommentsFromElement": false,
"applyGeneratedPreparationClasses": false,
"clearGeneratedClasses": false,
"blockTransitions": false,
"blockKeyframeAnimations": false,
"applyInlineStyle": false,
"concatWithSpace": false
/* ngAnimate directives/services */
"ngAnimateSwapDirective": true,
"$$rAFSchedulerFactory": true,
"$$AnimateChildrenDirective": true,
"$$AnimateQueueProvider": true,
"$$AnimationProvider": true,
"$AnimateCssProvider": true,
"$$AnimateCssDriverProvider": true,
"$$AnimateJsProvider": true,
"$$AnimateJsDriverProvider": true
}
}
+1 -1
View File
@@ -82,7 +82,7 @@ var $$AnimateChildrenDirective = ['$interpolate', function($interpolate) {
return {
link: function(scope, element, attrs) {
var val = attrs.ngAnimateChildren;
if (angular.isString(val) && val.length === 0) { //empty attribute
if (isString(val) && val.length === 0) { //empty attribute
element.data(NG_ANIMATE_CHILDREN_DATA, true);
} else {
// Interpolate and set the value, so that it is available to
+3 -4
View File
@@ -156,7 +156,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
}
);
var callbackRegistry = {};
var callbackRegistry = Object.create(null);
// remember that the classNameFilter is set during the provider/config
// stage therefore we can optimize here and setup a helper function
@@ -239,7 +239,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
},
off: function(event, container, callback) {
if (arguments.length === 1 && !angular.isString(arguments[0])) {
if (arguments.length === 1 && !isString(arguments[0])) {
container = arguments[0];
for (var eventType in callbackRegistry) {
callbackRegistry[eventType] = filterFromRegistry(callbackRegistry[eventType], container);
@@ -287,11 +287,10 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
bool = animationsEnabled = !!element;
} else {
var node = getDomNode(element);
var recordExists = disabledElementsLookup.get(node);
if (argCount === 1) {
// (element) - Element getter
bool = !recordExists;
bool = !disabledElementsLookup.get(node);
} else {
// (element, bool) - Element setter
disabledElementsLookup.put(node, !bool);
+29 -15
View File
@@ -1,19 +1,5 @@
'use strict';
/* global angularAnimateModule: true,
ngAnimateSwapDirective,
$$AnimateAsyncRunFactory,
$$rAFSchedulerFactory,
$$AnimateChildrenDirective,
$$AnimateQueueProvider,
$$AnimationProvider,
$AnimateCssProvider,
$$AnimateCssDriverProvider,
$$AnimateJsProvider,
$$AnimateJsDriverProvider,
*/
/**
* @ngdoc module
* @name ngAnimate
@@ -730,6 +716,19 @@
* (Note that you will need to trigger a digest within the callback to get angular to notice any scope-related changes.)
*/
var copy;
var extend;
var forEach;
var isArray;
var isDefined;
var isElement;
var isFunction;
var isObject;
var isString;
var isUndefined;
var jqLite;
var noop;
/**
* @ngdoc service
* @name $animate
@@ -740,7 +739,22 @@
*
* Click here {@link ng.$animate to learn more about animations with `$animate`}.
*/
angular.module('ngAnimate', [])
angular.module('ngAnimate', [], function initAngularHelpers() {
// Access helpers from angular core.
// Do it inside a `config` block to ensure `window.angular` is available.
noop = angular.noop;
copy = angular.copy;
extend = angular.extend;
jqLite = angular.element;
forEach = angular.forEach;
isArray = angular.isArray;
isString = angular.isString;
isObject = angular.isObject;
isUndefined = angular.isUndefined;
isDefined = angular.isDefined;
isFunction = angular.isFunction;
isElement = angular.isElement;
})
.directive('ngAnimateSwap', ngAnimateSwapDirective)
.directive('ngAnimateChildren', $$AnimateChildrenDirective)
+6 -26
View File
@@ -1,19 +1,5 @@
'use strict';
/* jshint ignore:start */
var noop = angular.noop;
var copy = angular.copy;
var extend = angular.extend;
var jqLite = angular.element;
var forEach = angular.forEach;
var isArray = angular.isArray;
var isString = angular.isString;
var isObject = angular.isObject;
var isUndefined = angular.isUndefined;
var isDefined = angular.isDefined;
var isFunction = angular.isFunction;
var isElement = angular.isElement;
var ELEMENT_NODE = 1;
var COMMENT_NODE = 8;
@@ -38,7 +24,7 @@ var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMA
// Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
// therefore there is no reason to test anymore for other vendor prefixes:
// http://caniuse.com/#search=transition
if (isUndefined(window.ontransitionend) && isDefined(window.onwebkittransitionend)) {
if ((window.ontransitionend === void 0) && (window.onwebkittransitionend !== void 0)) {
CSS_PREFIX = '-webkit-';
TRANSITION_PROP = 'WebkitTransition';
TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
@@ -47,7 +33,7 @@ if (isUndefined(window.ontransitionend) && isDefined(window.onwebkittransitionen
TRANSITIONEND_EVENT = 'transitionend';
}
if (isUndefined(window.onanimationend) && isDefined(window.onwebkitanimationend)) {
if ((window.onanimationend === void 0) && (window.onwebkitanimationend !== void 0)) {
CSS_PREFIX = '-webkit-';
ANIMATION_PROP = 'WebkitAnimation';
ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
@@ -69,10 +55,6 @@ var ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY;
var TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY;
var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY;
var isPromiseLike = function(p) {
return p && p.then ? true : false;
};
var ngMinErr = angular.$$minErr('ng');
function assertArg(arg, name, reason) {
if (!arg) {
@@ -128,7 +110,6 @@ function stripCommentsFromElement(element) {
switch (element.length) {
case 0:
return element;
break;
case 1:
// there is no point of stripping anything if the element
@@ -141,7 +122,6 @@ function stripCommentsFromElement(element) {
default:
return jqLite(extractElementNode(element));
break;
}
}
@@ -182,7 +162,7 @@ function applyAnimationClassesFactory($$jqLite) {
$$removeClass($$jqLite, element, options.removeClass);
options.removeClass = null;
}
}
};
}
function prepareAnimationOptions(options) {
@@ -285,10 +265,10 @@ function resolveElementClasses(existing, toAdd, toRemove) {
var prop, allow;
if (val === ADD_CLASS) {
prop = 'addClass';
allow = !existing[klass];
allow = !existing[klass] || existing[klass + REMOVE_CLASS_SUFFIX];
} else if (val === REMOVE_CLASS) {
prop = 'removeClass';
allow = existing[klass];
allow = existing[klass] || existing[klass + ADD_CLASS_SUFFIX];
}
if (allow) {
if (classes[prop].length) {
@@ -318,7 +298,7 @@ function resolveElementClasses(existing, toAdd, toRemove) {
}
function getDomNode(element) {
return (element instanceof angular.element) ? element[0] : element;
return (element instanceof jqLite) ? element[0] : element;
}
function applyGeneratedPreparationClasses(element, event, options) {
+3 -5
View File
@@ -5,11 +5,9 @@
// This file is compiled with Closure compiler's ADVANCED_OPTIMIZATIONS flag! Be wary of using
// constructs incompatible with that mode.
var $interpolateMinErr = window['angular']['$interpolateMinErr'];
var noop = window['angular']['noop'],
isFunction = window['angular']['isFunction'],
toJson = window['angular']['toJson'];
/* global isFunction: false */
/* global noop: false */
/* global toJson: false */
function stringify(value) {
if (value == null /* null/undefined */) { return ''; }
+132 -26
View File
@@ -5,31 +5,90 @@
// This file is compiled with Closure compiler's ADVANCED_OPTIMIZATIONS flag! Be wary of using
// constructs incompatible with that mode.
/* global $interpolateMinErr: false */
/* global $interpolateMinErr: true */
/* global isFunction: true */
/* global noop: true */
/* global toJson: true */
/* global MessageFormatParser: false */
/* global stringify: false */
/**
* @ngdoc service
* @name $$messageFormat
* @ngdoc module
* @name ngMessageFormat
* @packageName angular-message-format
*
* @description
* Angular internal service to recognize MessageFormat extensions in interpolation expressions.
* For more information, see:
* https://docs.google.com/a/google.com/document/d/1pbtW2yvtmFBikfRrJd8VAsabiFkKezmYZ_PbgdjQOVU/edit
*
* ## Example
* ## What is ngMessageFormat?
*
* <example name="ngMessageFormat-example" module="msgFmtExample" deps="angular-message-format.min.js">
* The ngMessageFormat module extends the Angular {@link ng.$interpolate `$interpolate`} service
* with a syntax for handling pluralization and gender specific messages, which is based on the
* [ICU MessageFormat syntax][ICU].
*
* See [the design doc][ngMessageFormat doc] for more information.
*
* [ICU]: http://userguide.icu-project.org/formatparse/messages#TOC-MessageFormat
* [ngMessageFormat doc]: https://docs.google.com/a/google.com/document/d/1pbtW2yvtmFBikfRrJd8VAsabiFkKezmYZ_PbgdjQOVU/edit
*
* ## Examples
*
* ### Gender
*
* This example uses the "select" keyword to specify the message based on gender.
*
* <example name="ngMessageFormat-example-gender" module="msgFmtExample" deps="angular-message-format.js">
* <file name="index.html">
* <div ng-controller="AppController">
* Select Recipient:<br>
<select ng-model="recipient" ng-options="person as person.name for person in recipients">
</select>
<p>{{recipient.gender, select,
male {{{recipient.name}} unwrapped his gift. }
female {{{recipient.name}} unwrapped her gift. }
other {{{recipient.name}} unwrapped their gift. }
}}</p>
* </div>
* </file>
* <file name="script.js">
* function Person(name, gender) {
* this.name = name;
* this.gender = gender;
* }
*
* var alice = new Person("Alice", "female"),
* bob = new Person("Bob", "male"),
* ashley = new Person("Ashley", "");
*
* angular.module('msgFmtExample', ['ngMessageFormat'])
* .controller('AppController', ['$scope', function($scope) {
* $scope.recipients = [alice, bob, ashley];
* $scope.recipient = $scope.recipients[0];
* }]);
* </file>
* </example>
*
* ### Plural
*
* This example shows how the "plural" keyword is used to account for a variable number of entities.
* The "#" variable holds the current number and can be embedded in the message.
*
* Note that "=1" takes precedence over "one".
*
* The example also shows the "offset" keyword, which allows you to offset the value of the "#" variable.
*
* <example name="ngMessageFormat-example-plural" module="msgFmtExample" deps="angular-message-format.js">
* <file name="index.html">
* <div ng-controller="AppController">
* <button ng-click="decreaseRecipients()" id="decreaseRecipients">decreaseRecipients</button><br>
* <span>{{recipients.length, plural, offset:1
* <button ng-click="recipients.pop()" id="decreaseRecipients">decreaseRecipients</button><br>
* Select recipients:<br>
* <select multiple size=5 ng-model="recipients" ng-options="person as person.name for person in people">
* </select><br>
* <p>{{recipients.length, plural, offset:1
* =0 {{{sender.name}} gave no gifts (\#=#)}
* =1 {{{sender.name}} gave one gift to {{recipients[0].name}} (\#=#)}
* =1 {{{sender.name}} gave a gift to {{recipients[0].name}} (\#=#)}
* one {{{sender.name}} gave {{recipients[0].name}} and one other person a gift (\#=#)}
* other {{{sender.name}} gave {{recipients[0].name}} and # other people a gift (\#=#)}
* }}</span>
* }}</p>
* </div>
* </file>
*
@@ -41,35 +100,79 @@
*
* var alice = new Person("Alice", "female"),
* bob = new Person("Bob", "male"),
* charlie = new Person("Charlie", "male"),
* harry = new Person("Harry Potter", "male");
* sarah = new Person("Sarah", "female"),
* harry = new Person("Harry Potter", "male"),
* ashley = new Person("Ashley", "");
*
* angular.module('msgFmtExample', ['ngMessageFormat'])
* .controller('AppController', ['$scope', function($scope) {
* $scope.recipients = [alice, bob, charlie];
* $scope.people = [alice, bob, sarah, ashley];
* $scope.recipients = [alice, bob, sarah];
* $scope.sender = harry;
* $scope.decreaseRecipients = function() {
* --$scope.recipients.length;
* };
* }]);
* </file>
*
* <file name="protractor.js" type="protractor">
* describe('MessageFormat plural', function() {
*
* it('should pluralize initial values', function() {
* var messageElem = element(by.binding('recipients.length')), decreaseRecipientsBtn = element(by.id('decreaseRecipients'));
* var messageElem = element(by.binding('recipients.length')),
* decreaseRecipientsBtn = element(by.id('decreaseRecipients'));
*
* expect(messageElem.getText()).toEqual('Harry Potter gave Alice and 2 other people a gift (#=2)');
* decreaseRecipientsBtn.click();
* expect(messageElem.getText()).toEqual('Harry Potter gave Alice and one other person a gift (#=1)');
* decreaseRecipientsBtn.click();
* expect(messageElem.getText()).toEqual('Harry Potter gave one gift to Alice (#=0)');
* expect(messageElem.getText()).toEqual('Harry Potter gave a gift to Alice (#=0)');
* decreaseRecipientsBtn.click();
* expect(messageElem.getText()).toEqual('Harry Potter gave no gifts (#=-1)');
* });
* });
* </file>
* </example>
*
* ### Plural and Gender together
*
* This example shows how you can specify gender rules for specific plural matches - in this case,
* =1 is special cased for gender.
* <example name="ngMessageFormat-example-plural-gender" module="msgFmtExample" deps="angular-message-format.js">
* <file name="index.html">
* <div ng-controller="AppController">
Select recipients:<br>
<select multiple size=5 ng-model="recipients" ng-options="person as person.name for person in people">
</select><br>
<p>{{recipients.length, plural,
=0 {{{sender.name}} has not given any gifts to anyone.}
=1 { {{recipients[0].gender, select,
female { {{sender.name}} gave {{recipients[0].name}} her gift.}
male { {{sender.name}} gave {{recipients[0].name}} his gift.}
other { {{sender.name}} gave {{recipients[0].name}} their gift.}
}}
}
other {{{sender.name}} gave {{recipients.length}} people gifts.}
}}</p>
</file>
* <file name="script.js">
* function Person(name, gender) {
* this.name = name;
* this.gender = gender;
* }
*
* var alice = new Person("Alice", "female"),
* bob = new Person("Bob", "male"),
* harry = new Person("Harry Potter", "male"),
* ashley = new Person("Ashley", "");
*
* angular.module('msgFmtExample', ['ngMessageFormat'])
* .controller('AppController', ['$scope', function($scope) {
* $scope.people = [alice, bob, ashley];
* $scope.recipients = [alice];
* $scope.sender = harry;
* }]);
* </file>
</example>
*/
var $$MessageFormatFactory = ['$parse', '$locale', '$sce', '$exceptionHandler', function $$messageFormat(
$parse, $locale, $sce, $exceptionHandler) {
@@ -107,15 +210,18 @@ var $$interpolateDecorator = ['$$messageFormat', '$delegate', function $$interpo
return interpolate;
}];
var $interpolateMinErr;
var isFunction;
var noop;
var toJson;
/**
* @ngdoc module
* @name ngMessageFormat
* @packageName angular-message-format
* @description
*/
var module = window['angular']['module']('ngMessageFormat', ['ng']);
module['factory']('$$messageFormat', $$MessageFormatFactory);
module['config'](['$provide', function($provide) {
$interpolateMinErr = window['angular']['$interpolateMinErr'];
isFunction = window['angular']['isFunction'];
noop = window['angular']['noop'];
toJson = window['angular']['toJson'];
$provide['decorator']('$interpolate', $$interpolateDecorator);
}]);
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "../../.jshintrc-base",
"globals": {
"window": false,
"angular": false
}
}
+12 -8
View File
@@ -1,12 +1,9 @@
'use strict';
/* jshint ignore:start */
// this code is in the core, but not in angular-messages.js
var isArray = angular.isArray;
var forEach = angular.forEach;
var isString = angular.isString;
var jqLite = angular.element;
/* jshint ignore:end */
var forEach;
var isArray;
var isString;
var jqLite;
/**
* @ngdoc module
@@ -262,7 +259,14 @@ var jqLite = angular.element;
*
* {@link ngAnimate Click here} to learn how to use JavaScript animations or to learn more about ngAnimate.
*/
angular.module('ngMessages', [])
angular.module('ngMessages', [], function initAngularHelpers() {
// Access helpers from angular core.
// Do it inside a `config` block to ensure `window.angular` is available.
forEach = angular.forEach;
isArray = angular.isArray;
isString = angular.isString;
jqLite = angular.element;
})
/**
* @ngdoc directive
+19 -12
View File
@@ -106,19 +106,29 @@ angular.mock.$Browser = function() {
* @param {number=} number of milliseconds to flush. See {@link #defer.now}
*/
self.defer.flush = function(delay) {
var nextTime;
if (angular.isDefined(delay)) {
self.defer.now += delay;
// A delay was passed so compute the next time
nextTime = self.defer.now + delay;
} else {
if (self.deferredFns.length) {
self.defer.now = self.deferredFns[self.deferredFns.length - 1].time;
// No delay was passed so set the next time so that it clears the deferred queue
nextTime = self.deferredFns[self.deferredFns.length - 1].time;
} else {
// No delay passed, but there are no deferred tasks so flush - indicates an error!
throw new Error('No deferred tasks to be flushed');
}
}
while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) {
while (self.deferredFns.length && self.deferredFns[0].time <= nextTime) {
// Increment the time and call the next deferred function
self.defer.now = self.deferredFns[0].time;
self.deferredFns.shift().fn();
}
// Ensure that the current time is correct
self.defer.now = nextTime;
};
self.$$baseHref = '/';
@@ -927,13 +937,10 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
* @name angular.mock.dump
* @description
*
* *NOTE*: this is not an injectable instance, just a globally available function.
* *NOTE*: This is not an injectable instance, just a globally available function.
*
* Method for serializing common angular objects (scope, elements, etc..) into strings, useful for
* debugging.
*
* This method is also available on window, where it can be used to display objects on debug
* console.
* Method for serializing common angular objects (scope, elements, etc..) into strings.
* It is useful for logging objects to the console when debugging.
*
* @param {*} object - any object to turn into string.
* @return {string} a serialized string of the argument
@@ -2274,7 +2281,7 @@ angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compi
* * [Google CDN](https://developers.google.com/speed/libraries/devguide#angularjs) e.g.
* `"//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-mocks.js"`
* * [NPM](https://www.npmjs.com/) e.g. `npm install angular-mocks@X.Y.Z`
* * [Bower](http://bower.io) e.g. `bower install angular-mocks@X.Y.Z`
* * [Bower](http://bower.io) e.g. `bower install angular-mocks#X.Y.Z`
* * [code.angularjs.org](https://code.angularjs.org/) (discouraged for production use) e.g.
* `"//code.angularjs.org/X.Y.Z/angular-mocks.js"`
*
@@ -2923,7 +2930,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
angular.forEach(angular.callbacks, function(val, key) {
delete angular.callbacks[key];
});
angular.callbacks.counter = 0;
angular.callbacks.$$counter = 0;
};
(window.beforeEach || window.setup)(module.$$beforeEach);
@@ -3026,7 +3033,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
this.stack = e.stack + '\n' + errorForStack.stack;
if (e.stackArray) this.stackArray = e.stackArray;
};
ErrorAddingDeclarationLocationStack.prototype.toString = Error.prototype.toString;
ErrorAddingDeclarationLocationStack.prototype = Error.prototype;
window.inject = angular.mock.inject = function() {
var blockFns = Array.prototype.slice.call(arguments, 0);
+12 -7
View File
@@ -114,8 +114,9 @@ function shallowClearAndCopy(src, dst) {
* can escape it with `/\.`.
*
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
* `actions` methods. If a parameter value is a function, it will be executed every time
* when a param value needs to be obtained for a request (unless the param was overridden).
* `actions` methods. If a parameter value is a function, it will be called every time
* a param value needs to be obtained for a request (unless the param was overridden). The function
* will be passed the current data value as an argument.
*
* Each key value in the parameter object is first bound to url template if present and then any
* excess keys are appended to the url search query after the `?`.
@@ -123,10 +124,13 @@ function shallowClearAndCopy(src, dst) {
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
* URL `/path/greet?salutation=Hello`.
*
* If the parameter value is prefixed with `@` then the value for that parameter will be extracted
* from the corresponding property on the `data` object (provided when calling an action method).
* If the parameter value is prefixed with `@`, then the value for that parameter will be
* extracted from the corresponding property on the `data` object (provided when calling a
* "non-GET" action method).
* For example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of
* `someParam` will be `data.someProp`.
* Note that the parameter will be ignored, when calling a "GET" action method (i.e. an action
* method that does not accept a request body)
*
* @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
* the default set of resource actions. The declaration should be created in the format of {@link
@@ -143,8 +147,9 @@ function shallowClearAndCopy(src, dst) {
* - **`method`** {string} Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
* `DELETE`, `JSONP`, etc).
* - **`params`** {Object=} Optional set of pre-bound parameters for this action. If any of
* the parameter value is a function, it will be executed every time when a param value needs to
* be obtained for a request (unless the param was overridden).
* the parameter value is a function, it will be called every time when a param value needs to
* be obtained for a request (unless the param was overridden). The function will be passed the
* current data value as an argument.
* - **`url`** {string} action specific `url` override. The url templating is supported just
* like for the resource-level urls.
* - **`isArray`** {boolean=} If true then the returned object for this action is an array,
@@ -641,7 +646,7 @@ angular.module('ngResource', ['ng']).
var ids = {};
actionParams = extend({}, paramDefaults, actionParams);
forEach(actionParams, function(value, key) {
if (isFunction(value)) { value = value(); }
if (isFunction(value)) { value = value(data); }
ids[key] = value && value.charAt && value.charAt(0) == '@' ?
lookupDottedPath(data, value.substr(1)) : value;
});
+7 -3
View File
@@ -2,9 +2,10 @@
/* global shallowCopy: false */
// There are necessary for `shallowCopy()` (included via `src/shallowCopy.js`)
var isArray = angular.isArray;
var isObject = angular.isObject;
// There are necessary for `shallowCopy()` (included via `src/shallowCopy.js`).
// They are initialized inside the `$RouteProvider`, to ensure `window.angular` is available.
var isArray;
var isObject;
/**
* @ngdoc module
@@ -41,6 +42,9 @@ var ngRouteModule = angular.module('ngRoute', ['ng']).
* Requires the {@link ngRoute `ngRoute`} module to be installed.
*/
function $RouteProvider() {
isArray = angular.isArray;
isObject = angular.isObject;
function inherit(parent, extra) {
return angular.extend(Object.create(parent), extra);
}
+1 -1
View File
@@ -4,6 +4,6 @@
"window": false,
"angular": false,
"htmlSanitizeWriter": false
"sanitizeText": false
}
}
+6 -5
View File
@@ -1,7 +1,5 @@
'use strict';
/* global sanitizeText: false */
/**
* @ngdoc filter
* @name linky
@@ -135,6 +133,9 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
MAILTO_REGEXP = /^mailto:/i;
var linkyMinErr = angular.$$minErr('linky');
var isDefined = angular.isDefined;
var isFunction = angular.isFunction;
var isObject = angular.isObject;
var isString = angular.isString;
return function(text, target, attributes) {
@@ -142,8 +143,8 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text);
var attributesFn =
angular.isFunction(attributes) ? attributes :
angular.isObject(attributes) ? function getAttributesObject() {return attributes;} :
isFunction(attributes) ? attributes :
isObject(attributes) ? function getAttributesObject() {return attributes;} :
function getEmptyAttributesObject() {return {};};
var match;
@@ -181,7 +182,7 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
html.push(key + '="' + linkAttributes[key] + '" ');
}
if (angular.isDefined(target) && !('target' in linkAttributes)) {
if (isDefined(target) && !('target' in linkAttributes)) {
html.push('target="',
target,
'" ');
+329 -309
View File
@@ -12,6 +12,14 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
var $sanitizeMinErr = angular.$$minErr('$sanitize');
var bind;
var extend;
var forEach;
var isDefined;
var lowercase;
var noop;
var htmlParser;
var htmlSanitizeWriter;
/**
* @ngdoc module
@@ -144,7 +152,7 @@ function $SanitizeProvider() {
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
if (svgEnabled) {
angular.extend(validElements, svgElements);
extend(validElements, svgElements);
}
return function(html) {
var buf = [];
@@ -187,328 +195,340 @@ function $SanitizeProvider() {
* without an argument or self for chaining otherwise.
*/
this.enableSvg = function(enableSvg) {
if (angular.isDefined(enableSvg)) {
if (isDefined(enableSvg)) {
svgEnabled = enableSvg;
return this;
} else {
return svgEnabled;
}
};
//////////////////////////////////////////////////////////////////////////////////////////////////
// Private stuff
//////////////////////////////////////////////////////////////////////////////////////////////////
bind = angular.bind;
extend = angular.extend;
forEach = angular.forEach;
isDefined = angular.isDefined;
lowercase = angular.lowercase;
noop = angular.noop;
htmlParser = htmlParserImpl;
htmlSanitizeWriter = htmlSanitizeWriterImpl;
// Regular Expressions for parsing tags and attributes
var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
// Match everything outside of normal chars and " (quote character)
NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;
// Good source of info about elements and attributes
// http://dev.w3.org/html5/spec/Overview.html#semantics
// http://simon.html5.org/html-elements
// Safe Void Elements - HTML5
// http://dev.w3.org/html5/spec/Overview.html#void-elements
var voidElements = toMap("area,br,col,hr,img,wbr");
// Elements that you can, intentionally, leave open (and which close themselves)
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
optionalEndTagInlineElements = toMap("rp,rt"),
optionalEndTagElements = extend({},
optionalEndTagInlineElements,
optionalEndTagBlockElements);
// Safe Block Elements - HTML5
var blockElements = extend({}, optionalEndTagBlockElements, toMap("address,article," +
"aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
"h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul"));
// Inline Elements - HTML5
var inlineElements = extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," +
"bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
"samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
// SVG Elements
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
// They can potentially allow for arbitrary javascript to be executed. See #11290
var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
"hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
"radialGradient,rect,stop,svg,switch,text,title,tspan");
// Blocked Elements (will be stripped)
var blockedElements = toMap("script,style");
var validElements = extend({},
voidElements,
blockElements,
inlineElements,
optionalEndTagElements);
//Attributes that have href and hence need to be sanitized
var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href");
var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
'valign,value,vspace,width');
// SVG attributes (without "id" and "name" attributes)
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
var validAttrs = extend({},
uriAttrs,
svgAttrs,
htmlAttrs);
function toMap(str, lowercaseKeys) {
var obj = {}, items = str.split(','), i;
for (i = 0; i < items.length; i++) {
obj[lowercaseKeys ? lowercase(items[i]) : items[i]] = true;
}
return obj;
}
var inertBodyElement;
(function(window) {
var doc;
if (window.document && window.document.implementation) {
doc = window.document.implementation.createHTMLDocument("inert");
} else {
throw $sanitizeMinErr('noinert', "Can't create an inert html document");
}
var docElement = doc.documentElement || doc.getDocumentElement();
var bodyElements = docElement.getElementsByTagName('body');
// usually there should be only one body element in the document, but IE doesn't have any, so we need to create one
if (bodyElements.length === 1) {
inertBodyElement = bodyElements[0];
} else {
var html = doc.createElement('html');
inertBodyElement = doc.createElement('body');
html.appendChild(inertBodyElement);
doc.appendChild(html);
}
})(window);
/**
* @example
* htmlParser(htmlString, {
* start: function(tag, attrs) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* });
*
* @param {string} html string
* @param {object} handler
*/
function htmlParserImpl(html, handler) {
if (html === null || html === undefined) {
html = '';
} else if (typeof html !== 'string') {
html = '' + html;
}
inertBodyElement.innerHTML = html;
//mXSS protection
var mXSSAttempts = 5;
do {
if (mXSSAttempts === 0) {
throw $sanitizeMinErr('uinput', "Failed to sanitize html because the input is unstable");
}
mXSSAttempts--;
// strip custom-namespaced attributes on IE<=11
if (window.document.documentMode) {
stripCustomNsAttrs(inertBodyElement);
}
html = inertBodyElement.innerHTML; //trigger mXSS
inertBodyElement.innerHTML = html;
} while (html !== inertBodyElement.innerHTML);
var node = inertBodyElement.firstChild;
while (node) {
switch (node.nodeType) {
case 1: // ELEMENT_NODE
handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
break;
case 3: // TEXT NODE
handler.chars(node.textContent);
break;
}
var nextNode;
if (!(nextNode = node.firstChild)) {
if (node.nodeType == 1) {
handler.end(node.nodeName.toLowerCase());
}
nextNode = node.nextSibling;
if (!nextNode) {
while (nextNode == null) {
node = node.parentNode;
if (node === inertBodyElement) break;
nextNode = node.nextSibling;
if (node.nodeType == 1) {
handler.end(node.nodeName.toLowerCase());
}
}
}
}
node = nextNode;
}
while (node = inertBodyElement.firstChild) {
inertBodyElement.removeChild(node);
}
}
function attrToMap(attrs) {
var map = {};
for (var i = 0, ii = attrs.length; i < ii; i++) {
var attr = attrs[i];
map[attr.name] = attr.value;
}
return map;
}
/**
* Escapes all potentially dangerous characters, so that the
* resulting string can be safely inserted into attribute or
* element text.
* @param value
* @returns {string} escaped text
*/
function encodeEntities(value) {
return value.
replace(/&/g, '&amp;').
replace(SURROGATE_PAIR_REGEXP, function(value) {
var hi = value.charCodeAt(0);
var low = value.charCodeAt(1);
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
}).
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
return '&#' + value.charCodeAt(0) + ';';
}).
replace(/</g, '&lt;').
replace(/>/g, '&gt;');
}
/**
* create an HTML/XML writer which writes to buffer
* @param {Array} buf use buf.join('') to get out sanitized html string
* @returns {object} in the form of {
* start: function(tag, attrs) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* }
*/
function htmlSanitizeWriterImpl(buf, uriValidator) {
var ignoreCurrentElement = false;
var out = bind(buf, buf.push);
return {
start: function(tag, attrs) {
tag = lowercase(tag);
if (!ignoreCurrentElement && blockedElements[tag]) {
ignoreCurrentElement = tag;
}
if (!ignoreCurrentElement && validElements[tag] === true) {
out('<');
out(tag);
forEach(attrs, function(value, key) {
var lkey = lowercase(key);
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
if (validAttrs[lkey] === true &&
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
out(' ');
out(key);
out('="');
out(encodeEntities(value));
out('"');
}
});
out('>');
}
},
end: function(tag) {
tag = lowercase(tag);
if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {
out('</');
out(tag);
out('>');
}
if (tag == ignoreCurrentElement) {
ignoreCurrentElement = false;
}
},
chars: function(chars) {
if (!ignoreCurrentElement) {
out(encodeEntities(chars));
}
}
};
}
/**
* When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare
* ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want
* to allow any of these custom attributes. This method strips them all.
*
* @param node Root element to process
*/
function stripCustomNsAttrs(node) {
if (node.nodeType === window.Node.ELEMENT_NODE) {
var attrs = node.attributes;
for (var i = 0, l = attrs.length; i < l; i++) {
var attrNode = attrs[i];
var attrName = attrNode.name.toLowerCase();
if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {
node.removeAttributeNode(attrNode);
i--;
l--;
}
}
}
var nextNode = node.firstChild;
if (nextNode) {
stripCustomNsAttrs(nextNode);
}
nextNode = node.nextSibling;
if (nextNode) {
stripCustomNsAttrs(nextNode);
}
}
}
function sanitizeText(chars) {
var buf = [];
var writer = htmlSanitizeWriter(buf, angular.noop);
var writer = htmlSanitizeWriter(buf, noop);
writer.chars(chars);
return buf.join('');
}
// Regular Expressions for parsing tags and attributes
var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
// Match everything outside of normal chars and " (quote character)
NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;
// Good source of info about elements and attributes
// http://dev.w3.org/html5/spec/Overview.html#semantics
// http://simon.html5.org/html-elements
// Safe Void Elements - HTML5
// http://dev.w3.org/html5/spec/Overview.html#void-elements
var voidElements = toMap("area,br,col,hr,img,wbr");
// Elements that you can, intentionally, leave open (and which close themselves)
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
optionalEndTagInlineElements = toMap("rp,rt"),
optionalEndTagElements = angular.extend({},
optionalEndTagInlineElements,
optionalEndTagBlockElements);
// Safe Block Elements - HTML5
var blockElements = angular.extend({}, optionalEndTagBlockElements, toMap("address,article," +
"aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
"h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul"));
// Inline Elements - HTML5
var inlineElements = angular.extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," +
"bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
"samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
// SVG Elements
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
// They can potentially allow for arbitrary javascript to be executed. See #11290
var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
"hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
"radialGradient,rect,stop,svg,switch,text,title,tspan");
// Blocked Elements (will be stripped)
var blockedElements = toMap("script,style");
var validElements = angular.extend({},
voidElements,
blockElements,
inlineElements,
optionalEndTagElements);
//Attributes that have href and hence need to be sanitized
var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href");
var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
'valign,value,vspace,width');
// SVG attributes (without "id" and "name" attributes)
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
var validAttrs = angular.extend({},
uriAttrs,
svgAttrs,
htmlAttrs);
function toMap(str, lowercaseKeys) {
var obj = {}, items = str.split(','), i;
for (i = 0; i < items.length; i++) {
obj[lowercaseKeys ? angular.lowercase(items[i]) : items[i]] = true;
}
return obj;
}
var inertBodyElement;
(function(window) {
var doc;
if (window.document && window.document.implementation) {
doc = window.document.implementation.createHTMLDocument("inert");
} else {
throw $sanitizeMinErr('noinert', "Can't create an inert html document");
}
var docElement = doc.documentElement || doc.getDocumentElement();
var bodyElements = docElement.getElementsByTagName('body');
// usually there should be only one body element in the document, but IE doesn't have any, so we need to create one
if (bodyElements.length === 1) {
inertBodyElement = bodyElements[0];
} else {
var html = doc.createElement('html');
inertBodyElement = doc.createElement('body');
html.appendChild(inertBodyElement);
doc.appendChild(html);
}
})(window);
/**
* @example
* htmlParser(htmlString, {
* start: function(tag, attrs) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* });
*
* @param {string} html string
* @param {object} handler
*/
function htmlParser(html, handler) {
if (html === null || html === undefined) {
html = '';
} else if (typeof html !== 'string') {
html = '' + html;
}
inertBodyElement.innerHTML = html;
//mXSS protection
var mXSSAttempts = 5;
do {
if (mXSSAttempts === 0) {
throw $sanitizeMinErr('uinput', "Failed to sanitize html because the input is unstable");
}
mXSSAttempts--;
// strip custom-namespaced attributes on IE<=11
if (window.document.documentMode) {
stripCustomNsAttrs(inertBodyElement);
}
html = inertBodyElement.innerHTML; //trigger mXSS
inertBodyElement.innerHTML = html;
} while (html !== inertBodyElement.innerHTML);
var node = inertBodyElement.firstChild;
while (node) {
switch (node.nodeType) {
case 1: // ELEMENT_NODE
handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
break;
case 3: // TEXT NODE
handler.chars(node.textContent);
break;
}
var nextNode;
if (!(nextNode = node.firstChild)) {
if (node.nodeType == 1) {
handler.end(node.nodeName.toLowerCase());
}
nextNode = node.nextSibling;
if (!nextNode) {
while (nextNode == null) {
node = node.parentNode;
if (node === inertBodyElement) break;
nextNode = node.nextSibling;
if (node.nodeType == 1) {
handler.end(node.nodeName.toLowerCase());
}
}
}
}
node = nextNode;
}
while (node = inertBodyElement.firstChild) {
inertBodyElement.removeChild(node);
}
}
function attrToMap(attrs) {
var map = {};
for (var i = 0, ii = attrs.length; i < ii; i++) {
var attr = attrs[i];
map[attr.name] = attr.value;
}
return map;
}
/**
* Escapes all potentially dangerous characters, so that the
* resulting string can be safely inserted into attribute or
* element text.
* @param value
* @returns {string} escaped text
*/
function encodeEntities(value) {
return value.
replace(/&/g, '&amp;').
replace(SURROGATE_PAIR_REGEXP, function(value) {
var hi = value.charCodeAt(0);
var low = value.charCodeAt(1);
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
}).
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
return '&#' + value.charCodeAt(0) + ';';
}).
replace(/</g, '&lt;').
replace(/>/g, '&gt;');
}
/**
* create an HTML/XML writer which writes to buffer
* @param {Array} buf use buf.join('') to get out sanitized html string
* @returns {object} in the form of {
* start: function(tag, attrs) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* }
*/
function htmlSanitizeWriter(buf, uriValidator) {
var ignoreCurrentElement = false;
var out = angular.bind(buf, buf.push);
return {
start: function(tag, attrs) {
tag = angular.lowercase(tag);
if (!ignoreCurrentElement && blockedElements[tag]) {
ignoreCurrentElement = tag;
}
if (!ignoreCurrentElement && validElements[tag] === true) {
out('<');
out(tag);
angular.forEach(attrs, function(value, key) {
var lkey=angular.lowercase(key);
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
if (validAttrs[lkey] === true &&
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
out(' ');
out(key);
out('="');
out(encodeEntities(value));
out('"');
}
});
out('>');
}
},
end: function(tag) {
tag = angular.lowercase(tag);
if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {
out('</');
out(tag);
out('>');
}
if (tag == ignoreCurrentElement) {
ignoreCurrentElement = false;
}
},
chars: function(chars) {
if (!ignoreCurrentElement) {
out(encodeEntities(chars));
}
}
};
}
/**
* When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare
* ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want
* to allow any of these custom attributes. This method strips them all.
*
* @param node Root element to process
*/
function stripCustomNsAttrs(node) {
if (node.nodeType === window.Node.ELEMENT_NODE) {
var attrs = node.attributes;
for (var i = 0, l = attrs.length; i < l; i++) {
var attrNode = attrs[i];
var attrName = attrNode.name.toLowerCase();
if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {
node.removeAttributeNode(attrNode);
i--;
l--;
}
}
}
var nextNode = node.firstChild;
if (nextNode) {
stripCustomNsAttrs(nextNode);
}
nextNode = node.nextSibling;
if (nextNode) {
stripCustomNsAttrs(nextNode);
}
}
// define ngSanitize module and register $sanitize service
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
+14 -8
View File
@@ -35,6 +35,12 @@ ngTouch.factory('$swipe', [function() {
move: 'touchmove',
end: 'touchend',
cancel: 'touchcancel'
},
'pointer': {
start: 'pointerdown',
move: 'pointermove',
end: 'pointerup',
cancel: 'pointercancel'
}
};
@@ -69,15 +75,15 @@ ngTouch.factory('$swipe', [function() {
* The main method of `$swipe`. It takes an element to be watched for swipe motions, and an
* object containing event handlers.
* The pointer types that should be used can be specified via the optional
* third argument, which is an array of strings `'mouse'` and `'touch'`. By default,
* `$swipe` will listen for `mouse` and `touch` events.
* third argument, which is an array of strings `'mouse'`, `'touch'` and `'pointer'`. By default,
* `$swipe` will listen for `mouse`, `touch` and `pointer` events.
*
* The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end`
* receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }` and the raw
* `event`. `cancel` receives the raw `event` as its single parameter.
*
* `start` is called on either `mousedown` or `touchstart`. After this event, `$swipe` is
* watching for `touchmove` or `mousemove` events. These events are ignored until the total
* `start` is called on either `mousedown`, `touchstart` or `pointerdown`. After this event, `$swipe` is
* watching for `touchmove`, `mousemove` or `pointermove` events. These events are ignored until the total
* distance moved in either dimension exceeds a small threshold.
*
* Once this threshold is exceeded, either the horizontal or vertical delta is greater.
@@ -85,12 +91,12 @@ ngTouch.factory('$swipe', [function() {
* - If the vertical distance is greater, this is a scroll, and we let the browser take over.
* A `cancel` event is sent.
*
* `move` is called on `mousemove` and `touchmove` after the above logic has determined that
* `move` is called on `mousemove`, `touchmove` and `pointermove` after the above logic has determined that
* a swipe is in progress.
*
* `end` is called when a swipe is successfully completed with a `touchend` or `mouseup`.
* `end` is called when a swipe is successfully completed with a `touchend`, `mouseup` or `pointerup`.
*
* `cancel` is called either on a `touchcancel` from the browser, or when we begin scrolling
* `cancel` is called either on a `touchcancel` or `pointercancel` from the browser, or when we begin scrolling
* as described above.
*
*/
@@ -104,7 +110,7 @@ ngTouch.factory('$swipe', [function() {
// Whether a swipe is active.
var active = false;
pointerTypes = pointerTypes || ['mouse', 'touch'];
pointerTypes = pointerTypes || ['mouse', 'touch', 'pointer'];
element.on(getEvents(pointerTypes, 'start'), function(event) {
startCoords = getCoordinates(event);
active = true;
+14
View File
@@ -240,6 +240,20 @@ describe('angular', function() {
}
});
it("should handle Uint16Array subarray", function() {
if (typeof Uint16Array !== 'undefined') {
var arr = new Uint16Array(4);
arr[1] = 1;
var src = arr.subarray(1, 2);
var dst = copy(src);
expect(dst instanceof Uint16Array).toBeTruthy();
expect(dst.length).toEqual(1);
expect(dst[0]).toEqual(1);
expect(dst).not.toBe(src);
expect(dst.buffer).not.toBe(src.buffer);
}
});
it("should throw an exception if a Uint8Array is the destination", function() {
if (typeof Uint8Array !== 'undefined') {
var src = new Uint8Array();
+20
View File
@@ -302,6 +302,26 @@ describe('injector', function() {
expect(instance.aVal()).toEqual('a-value');
});
if (/chrome/.test(navigator.userAgent)) {
they('should detect ES6 classes regardless of whitespace/comments ($prop)', [
'class Test {}',
'class Test{}',
'class //<--ES6 stuff\nTest {}',
'class//<--ES6 stuff\nTest {}',
'class {}',
'class{}',
'class //<--ES6 stuff\n {}',
'class//<--ES6 stuff\n {}',
'class/* Test */{}',
'class /* Test */ {}'
], function(classDefinition) {
var Clazz = eval('(' + classDefinition + ')');
var instance = injector.invoke(Clazz);
expect(instance).toEqual(jasmine.any(Clazz));
});
}
// Support: Chrome 50-51 only
// TODO (gkalpak): Remove when Chrome v52 is relased.
// it('should be able to invoke classes', function() {
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html ng-app="test">
<body>
<div class="fixed-header" ng-controller="TestController">
<button ng-click="scrollTo('anchor-' + x)" ng-repeat="x in [1, 2, 3, 4, 5]">
Scroll to anchor-{{x}}
</button>
</div>
<div class="anchor" id="anchor-{{y}}" ng-repeat="y in [1, 2, 3, 4, 5]">
Anchor {{y}} of 5
</div>
<style type="text/css">
body {
height: 100%;
margin: 0;
padding-top: 50px;
}
.anchor {
border: 2px dashed darkorchid;
padding: 10px 10px 390px 10px;
}
.fixed-header {
background-color: rgba(0, 0, 0, 0.2);
height: 50px;
position: fixed;
top: 0; left: 0; right: 0;
}
.fixed-header > button {
display: inline-block;
margin: 5px 15px;
}
</style>
<script src="angular.js"></script>
<script src="script.js"></script>
</body>
</html>
@@ -0,0 +1,17 @@
angular.
module('test', []).
controller('TestController', function($anchorScroll, $location, $scope) {
$anchorScroll.yOffset = 50;
$scope.scrollTo = function(target) {
if ($location.hash() !== target) {
// Set `$location.hash()` to `target` and
// `$anchorScroll` will detect the change and scroll
$location.hash(target);
} else {
// The hash is the same, but `target` might be out of view -
// explicitly call `$anchorScroll`
$anchorScroll();
}
};
});
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html ng-app="test">
<body>
<div class="scroll-area" ng-controller="TestController">
<a id="top" ng-click="scrollTo('bottom')">Go to bottom</a>
<a id="bottom" ng-click="scrollTo('top')">Back to top</a>
</div>
<style type="text/css">
.scroll-area {
height: 200px;
overflow: auto;
}
#bottom {
display: block;
margin-top: 2000px;
}
</style>
<script src="angular.js"></script>
<script src="script.js"></script>
</body>
</html>
@@ -0,0 +1,9 @@
angular.
module('test', []).
controller('TestController', function($anchorScroll, $location, $scope) {
$scope.scrollTo = function(target) {
// Set `$location.hash()` to `target` and
// `$anchorScroll` will detect the change and scroll
$location.hash(target);
};
});
@@ -1,10 +1,12 @@
<!DOCTYPE html>
<html ng-app="test">
<div ng-controller="TestCtrl">
<p>{{text}}</p>
</div>
<body>
<div ng-controller="TestController">
<p>{{text}}</p>
</div>
<script src="angular.js"></script>
<script src="angular.js"></script>
<script src="script.js"></script>
<script src="angular.js"></script>
<script src="angular.js"></script>
<script src="script.js"></script>
</body>
</html>
+4 -3
View File
@@ -1,4 +1,5 @@
angular.module("test", []).
controller("TestCtrl", function($scope) {
$scope.text = "Hello, world!";
angular.
module('test', []).
controller('TestController', function($scope) {
$scope.text = 'Hello, world!';
});
+23
View File
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html ng-app="test">
<body>
<div ng-controller="TestController">
<p>{{text}}</p>
</div>
<!-- Load modules before `angular.js` -->
<script src="angular-loader.js"></script>
<script src="angular-touch.js"></script>
<script src="angular-sanitize.js"></script>
<script src="angular-route.js"></script>
<script src="angular-resource.js"></script>
<script src="angular-parse-ext.js"></script>
<script src="angular-messages.js"></script>
<script src="angular-message-format.js"></script>
<script src="angular-cookies.js"></script>
<script src="angular-aria.js"></script>
<script src="angular-animate.js"></script>
<script src="angular.js"></script>
<script src="script.js"></script>
</body>
</html>
+16
View File
@@ -0,0 +1,16 @@
angular.
module('test', [
'ngTouch',
'ngSanitize',
'ngRoute',
'ngResource',
'ngParseExt',
'ngMessages',
'ngMessageFormat',
'ngCookies',
'ngAria',
'ngAnimate'
]).
controller('TestController', function($scope) {
$scope.text = 'Hello, world!';
});
+14
View File
@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html ng-app="test" ng-jq="jQuery_2_1_0">
<body>
{{jqueryVersion}}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script>
var jQuery_2_1_0 = jQuery.noConflict();
</script>
<script src="../../../../bower_components/jquery/dist/jquery.js"></script>
<script src="angular.js"></script>
<script src="script.js"></script>
</body>
</html>
@@ -1,4 +1,5 @@
angular.module('test', [])
.run(function($rootScope) {
angular.
module('test', []).
run(function($rootScope) {
$rootScope.jqueryVersion = window.angular.element().jquery || 'jqLite';
});
@@ -10,6 +10,6 @@
window[''] = window.jQuery;
</script>
<script src="angular.js"></script>
<script type="text/javascript" src="script.js"></script>
<script src="script.js"></script>
</body>
</html>
@@ -1,4 +1,5 @@
angular.module('test', [])
.run(function($rootScope) {
angular.
module('test', []).
run(function($rootScope) {
$rootScope.jqueryVersion = window.angular.element().jquery || 'jqLite';
});
-14
View File
@@ -1,14 +0,0 @@
<!DOCTYPE html>
<html ng-app="test" ng-jq="jQuery_2_1_0">
<body>
{{jqueryVersion}}
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<script>
var jQuery_2_1_0 = jQuery.noConflict();
</script>
<script src="../../../../bower_components/jquery/dist/jquery.js"></script>
<script src="angular.js"></script>
<script type="text/javascript" src="script.js"></script>
</body>
</html>
+7 -5
View File
@@ -1,9 +1,11 @@
<!DOCTYPE html>
<html ng-app="test">
<div ng-controller="TestCtrl">
<p>{{text}}</p>
</div>
<body>
<div ng-controller="TestController">
<p>{{text}}</p>
</div>
<script src="angular.js"></script>
<script src="script.js"></script>
<script src="angular.js"></script>
<script src="script.js"></script>
</body>
</html>
+4 -3
View File
@@ -1,4 +1,5 @@
angular.module("test", []).
controller("TestCtrl", function($scope) {
$scope.text = "Hello, world!";
angular.
module('test', []).
controller('TestController', function($scope) {
$scope.text = 'Hello, world!';
});
+187
View File
@@ -0,0 +1,187 @@
describe('$anchorScroll', function() {
beforeEach(function() {
jasmine.addMatchers({
toBeInViewport: function() {
return {
compare: function(id) {
var result = {
pass: browser.driver.
executeScript(_script_isInViewport, id).
then(function(isInViewport) {
result.message = 'Expected #' + id + (isInViewport ? ' not' : '') +
' to be in viewport';
return isInViewport;
})
};
return result;
}
};
},
toHaveTop: function() {
return {
compare: function(id, expectedTop) {
var result = {
pass: browser.driver.
executeScript(_script_getTop, id).
then(function(actualTop) {
var passed = actualTop === expectedTop;
result.message = 'Expected #' + id + '\'s top' + (passed ? ' not' : '') +
' to be ' + expectedTop + ', but it was ' + actualTop;
return passed;
})
};
return result;
}
};
}
});
});
describe('basic functionality', function() {
beforeEach(function() {
loadFixture('anchor-scroll');
});
it('should scroll to #bottom when clicking #top and vice versa', function() {
expect('top').toBeInViewport();
expect('bottom').not.toBeInViewport();
element(by.id('top')).click();
expect('top').not.toBeInViewport();
expect('bottom').toBeInViewport();
element(by.id('bottom')).click();
expect('top').toBeInViewport();
expect('bottom').not.toBeInViewport();
});
});
describe('with `yOffset`', function() {
var yOffset = 50;
var buttons = element.all(by.repeater('x in [1, 2, 3, 4, 5]'));
var anchors = element.all(by.repeater('y in [1, 2, 3, 4, 5]'));
beforeEach(function() {
loadFixture('anchor-scroll-y-offset');
});
it('should scroll to the correct anchor when clicking each button', function() {
var lastAnchor = anchors.last();
// Make sure there is enough room to scroll the last anchor to the top
lastAnchor.getSize().then(function(size) {
var tempHeight = size.height - 10;
execWithTempViewportHeight(tempHeight, function() {
buttons.each(function(button, idx) {
// For whatever reason, we need to run the assertions inside a callback :(
button.click().then(function() {
var anchorId = 'anchor-' + (idx + 1);
expect(anchorId).toBeInViewport();
expect(anchorId).toHaveTop(yOffset);
});
});
});
});
});
it('should automatically scroll when navigating to a URL with a hash', function() {
var lastAnchor = anchors.last();
var lastAnchorId = 'anchor-5';
// Make sure there is enough room to scroll the last anchor to the top
lastAnchor.getSize().then(function(size) {
var tempHeight = size.height - 10;
execWithTempViewportHeight(tempHeight, function() {
// Test updating `$location.url()` from within the app
expect(lastAnchorId).not.toBeInViewport();
browser.setLocation('#' + lastAnchorId);
expect(lastAnchorId).toBeInViewport();
expect(lastAnchorId).toHaveTop(yOffset);
// Test navigating to the URL directly
scrollToTop();
expect(lastAnchorId).not.toBeInViewport();
browser.refresh();
expect(lastAnchorId).toBeInViewport();
expect(lastAnchorId).toHaveTop(yOffset);
});
});
});
it('should not scroll "overzealously"', function() {
var lastButton = buttons.last();
var lastAnchor = anchors.last();
var lastAnchorId = 'anchor-5';
// Make sure there is not enough room to scroll the last anchor to the top
lastAnchor.getSize().then(function(size) {
var tempHeight = size.height + (yOffset / 2);
execWithTempViewportHeight(tempHeight, function() {
scrollIntoView(lastAnchorId);
expect(lastAnchorId).toHaveTop(yOffset / 2);
lastButton.click();
expect(lastAnchorId).toBeInViewport();
expect(lastAnchorId).toHaveTop(yOffset);
});
});
});
});
// Helpers
function _script_getTop(id) {
var elem = document.getElementById(id);
var rect = elem.getBoundingClientRect();
return rect.top;
}
function _script_isInViewport(id) {
var elem = document.getElementById(id);
var rect = elem.getBoundingClientRect();
var docElem = document.documentElement;
return (rect.top < docElem.clientHeight) &&
(rect.bottom > 0) &&
(rect.left < docElem.clientWidth) &&
(rect.right > 0);
}
function execWithTempViewportHeight(tempHeight, fn) {
setViewportHeight(tempHeight).then(function(oldHeight) {
fn();
setViewportHeight(oldHeight);
});
}
function scrollIntoView(id) {
browser.driver.executeScript('document.getElementById("' + id + '").scrollIntoView()');
}
function scrollToTop() {
browser.driver.executeScript('window.scrollTo(0, 0)');
}
function setViewportHeight(newHeight) {
return browser.driver.
executeScript('return document.documentElement.clientHeight').
then(function(oldHeight) {
var heightDiff = newHeight - oldHeight;
var win = browser.driver.manage().window();
return win.getSize().then(function(size) {
return win.
setSize(size.width, size.height + heightDiff).
then(function() { return oldHeight; });
});
});
}
});
@@ -1,10 +1,9 @@
describe('App where angular is loaded more than once', function() {
beforeEach(function() {
loadFixture("angular-already-loaded").andWaitForAngular();
loadFixture('angular-already-loaded');
});
it('should have the interpolated text', function() {
expect(element(by.binding('text')).getText())
.toBe('Hello, world!');
expect(element(by.binding('text')).getText()).toBe('Hello, world!');
});
});
-3
View File
@@ -1,7 +1,4 @@
var helper = {
andWaitForAngular: function() {
browser.waitForAngular();
},
loadFixture: function(fixture) {
var i = 0;
while (fixture[i] === '/') ++i;
+9
View File
@@ -0,0 +1,9 @@
describe('angular-loader', function() {
beforeEach(function() {
loadFixture('loader');
});
it('should not be broken by loading the modules before core', function() {
expect(element(by.binding('text')).getText()).toBe('Hello, world!');
});
});
@@ -1,12 +1,11 @@
describe('Customizing the jqlite / jquery version', function() {
it('should be able to force jqlite', function() {
loadFixture("ngJq").andWaitForAngular();
describe('Customizing the jqLite / jQuery version', function() {
it('should be able to force jqLite', function() {
loadFixture('ng-jq');
expect(element(by.binding('jqueryVersion')).getText()).toBe('jqLite');
});
it('should be able to use a specific version jQuery', function() {
loadFixture("ngJqJquery").andWaitForAngular();
loadFixture('ng-jq-jquery');
expect(element(by.binding('jqueryVersion')).getText()).toBe('2.1.0');
});
});
@@ -1,12 +1,10 @@
// Sample E2E test:
//
describe('Sample', function() {
beforeEach(function() {
loadFixture("sample").andWaitForAngular();
loadFixture('sample');
});
it('should have the interpolated text', function() {
expect(element(by.binding('text')).getText())
.toBe('Hello, world!');
expect(element(by.binding('text')).getText()).toBe('Hello, world!');
});
});
+227 -34
View File
@@ -3816,6 +3816,96 @@ describe('$compile', function() {
});
});
describe('$doCheck', function() {
it('should call `$doCheck`, if provided, for each digest cycle, after $onChanges and $onInit', function() {
var log = [];
function TestController() { }
TestController.prototype.$doCheck = function() { log.push('$doCheck'); };
TestController.prototype.$onChanges = function() { log.push('$onChanges'); };
TestController.prototype.$onInit = function() { log.push('$onInit'); };
angular.module('my', [])
.component('dcc', {
controller: TestController,
bindings: { 'prop1': '<' }
});
module('my');
inject(function($compile, $rootScope) {
element = $compile('<dcc prop1="val"></dcc>')($rootScope);
expect(log).toEqual([
'$onChanges',
'$onInit',
'$doCheck'
]);
// Clear log
log = [];
$rootScope.$apply();
expect(log).toEqual([
'$doCheck',
'$doCheck'
]);
// Clear log
log = [];
$rootScope.$apply('val = 2');
expect(log).toEqual([
'$doCheck',
'$onChanges',
'$doCheck'
]);
});
});
it('should work if $doCheck is provided in the constructor', function() {
var log = [];
function TestController() {
this.$doCheck = function() { log.push('$doCheck'); };
this.$onChanges = function() { log.push('$onChanges'); };
this.$onInit = function() { log.push('$onInit'); };
}
angular.module('my', [])
.component('dcc', {
controller: TestController,
bindings: { 'prop1': '<' }
});
module('my');
inject(function($compile, $rootScope) {
element = $compile('<dcc prop1="val"></dcc>')($rootScope);
expect(log).toEqual([
'$onChanges',
'$onInit',
'$doCheck'
]);
// Clear log
log = [];
$rootScope.$apply();
expect(log).toEqual([
'$doCheck',
'$doCheck'
]);
// Clear log
log = [];
$rootScope.$apply('val = 2');
expect(log).toEqual([
'$doCheck',
'$onChanges',
'$doCheck'
]);
});
});
});
describe('$onChanges', function() {
@@ -7889,7 +7979,7 @@ describe('$compile', function() {
directive('trans', function() {
return {
transclude: true,
template: '<div ng-transclude>old stuff! </div>'
template: '<div ng-transclude>old stuff!</div>'
};
});
});
@@ -7906,18 +7996,152 @@ describe('$compile', function() {
directive('trans', function() {
return {
transclude: true,
template: '<div ng-transclude>old stuff! </div>'
template: '<div ng-transclude>old stuff!</div>'
};
});
});
inject(function(log, $rootScope, $compile) {
element = $compile('<div trans></div>')($rootScope);
$rootScope.$apply();
expect(sortedHtml(element.html())).toEqual('<div ng-transclude="">old stuff! </div>');
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""><span>old stuff!</span></div>');
});
});
it('should clear the fallback content from the element during compile and before linking', function() {
module(function() {
directive('trans', function() {
return {
transclude: true,
template: '<div ng-transclude>fallback content</div>'
};
});
});
inject(function(log, $rootScope, $compile) {
element = jqLite('<div trans></div>');
var linkfn = $compile(element);
expect(element.html()).toEqual('<div ng-transclude=""></div>');
linkfn($rootScope);
$rootScope.$apply();
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""><span>fallback content</span></div>');
});
});
it('should allow cloning of the fallback via ngRepeat', function() {
module(function() {
directive('trans', function() {
return {
transclude: true,
template: '<div ng-repeat="i in [0,1,2]"><div ng-transclude>{{i}}</div></div>'
};
});
});
inject(function(log, $rootScope, $compile) {
element = $compile('<div trans></div>')($rootScope);
$rootScope.$apply();
expect(element.text()).toEqual('012');
});
});
it('should not link the fallback content if transcluded content is provided', function() {
var linkSpy = jasmine.createSpy('postlink');
module(function() {
directive('inner', function() {
return {
restrict: 'E',
template: 'old stuff! ',
link: linkSpy
};
});
directive('trans', function() {
return {
transclude: true,
template: '<div ng-transclude><inner></inner></div>'
};
});
});
inject(function($rootScope, $compile) {
element = $compile('<div trans>unicorn!</div>')($rootScope);
$rootScope.$apply();
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""><span>unicorn!</span></div>');
expect(linkSpy).not.toHaveBeenCalled();
});
});
it('should compile and link the fallback content if no transcluded content is provided', function() {
var linkSpy = jasmine.createSpy('postlink');
module(function() {
directive('inner', function() {
return {
restrict: 'E',
template: 'old stuff! ',
link: linkSpy
};
});
directive('trans', function() {
return {
transclude: true,
template: '<div ng-transclude><inner></inner></div>'
};
});
});
inject(function(log, $rootScope, $compile) {
element = $compile('<div trans></div>')($rootScope);
$rootScope.$apply();
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""><inner>old stuff! </inner></div>');
expect(linkSpy).toHaveBeenCalled();
});
});
it('should compile and link the fallback content if an optional transclusion slot is not provided', function() {
var linkSpy = jasmine.createSpy('postlink');
module(function() {
directive('inner', function() {
return {
restrict: 'E',
template: 'old stuff! ',
link: linkSpy
};
});
directive('trans', function() {
return {
transclude: { optionalSlot: '?optional'},
template: '<div ng-transclude="optionalSlot"><inner></inner></div>'
};
});
});
inject(function(log, $rootScope, $compile) {
element = $compile('<div trans></div>')($rootScope);
$rootScope.$apply();
expect(sortedHtml(element.html())).toEqual('<div ng-transclude="optionalSlot"><inner>old stuff! </inner></div>');
expect(linkSpy).toHaveBeenCalled();
});
});
it('should cope if there is neither transcluded content nor fallback content', function() {
module(function() {
directive('trans', function() {
return {
transclude: true,
template: '<div ng-transclude></div>'
};
});
});
inject(function($rootScope, $compile) {
element = $compile('<div trans></div>')($rootScope);
$rootScope.$apply();
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""></div>');
});
});
it('should throw on an ng-transclude element inside no transclusion directive', function() {
inject(function($rootScope, $compile) {
// we need to do this because different browsers print empty attributes differently
@@ -9716,37 +9940,6 @@ describe('$compile', function() {
expect(element.children().eq(2).text()).toEqual('dorothy');
});
});
it('should not overwrite the contents of an `ng-transclude` element, if the matching optional slot is not filled', function() {
module(function() {
directive('minionComponent', function() {
return {
restrict: 'E',
scope: {},
transclude: {
minionSlot: 'minion',
bossSlot: '?boss'
},
template:
'<div class="boss" ng-transclude="bossSlot">default boss content</div>' +
'<div class="minion" ng-transclude="minionSlot">default minion content</div>' +
'<div class="other" ng-transclude>default content</div>'
};
});
});
inject(function($rootScope, $compile) {
element = $compile(
'<minion-component>' +
'<minion>stuart</minion>' +
'<span>dorothy</span>' +
'<minion>kevin</minion>' +
'</minion-component>')($rootScope);
$rootScope.$apply();
expect(element.children().eq(0).text()).toEqual('default boss content');
expect(element.children().eq(1).text()).toEqual('stuartkevin');
expect(element.children().eq(2).text()).toEqual('dorothy');
});
});
});
+81
View File
@@ -1789,6 +1789,87 @@ describe('ngOptions', function() {
});
it('should group if the group has a falsy value (except undefined)', function() {
createSelect({
'ng-model': 'selected',
'ng-options': 'item.name group by item.group for item in values'
});
scope.$apply(function() {
scope.values = [{name: 'A'},
{name: 'B', group: ''},
{name: 'C', group: null},
{name: 'D', group: false},
{name: 'E', group: 0}];
scope.selected = scope.values[0];
});
var optgroups = element.find('optgroup');
var options = element.find('option');
expect(optgroups.length).toEqual(4);
expect(options.length).toEqual(5);
expect(optgroups[0].label).toBe('');
expect(optgroups[1].label).toBe('null');
expect(optgroups[2].label).toBe('false');
expect(optgroups[3].label).toBe('0');
expect(options[0].textContent).toBe('A');
expect(options[0].parentNode).toBe(element[0]);
expect(options[1].textContent).toBe('B');
expect(options[1].parentNode).toBe(optgroups[0]);
expect(options[2].textContent).toBe('C');
expect(options[2].parentNode).toBe(optgroups[1]);
expect(options[3].textContent).toBe('D');
expect(options[3].parentNode).toBe(optgroups[2]);
expect(options[4].textContent).toBe('E');
expect(options[4].parentNode).toBe(optgroups[3]);
});
it('should not duplicate a group with a falsy value when the options are updated', function() {
scope.$apply(function() {
scope.values = [{value: 'A', group: ''},
{value: 'B', group: 'First'}];
scope.selected = scope.values[0];
});
createSelect({
'ng-model': 'selected',
'ng-options': 'item.value group by item.group for item in values'
});
scope.$apply(function() {
scope.values.push({value: 'C', group: false});
});
var optgroups = element.find('optgroup');
var options = element.find('option');
expect(optgroups.length).toEqual(3);
expect(options.length).toEqual(3);
expect(optgroups[0].label).toBe('');
expect(optgroups[1].label).toBe('First');
expect(optgroups[2].label).toBe('false');
expect(options[0].textContent).toBe('A');
expect(options[0].parentNode).toBe(optgroups[0]);
expect(options[1].textContent).toBe('B');
expect(options[1].parentNode).toBe(optgroups[1]);
expect(options[2].textContent).toBe('C');
expect(options[2].parentNode).toBe(optgroups[2]);
});
it('should bind to scope value and track/identify objects', function() {
createSelect({
'ng-model': 'selected',
+20 -1
View File
@@ -192,6 +192,25 @@ describe('Filter: filter', function() {
});
it('should allow specifying the special "match-all" property', function() {
var items = [
{foo: 'baz'},
{bar: 'baz'},
{'%': 'no dollar'}
];
expect(filter(items, {$: 'baz'}).length).toBe(2);
expect(filter(items, {$: 'baz'}, null, '%').length).toBe(0);
expect(filter(items, {'%': 'dollar'}).length).toBe(1);
expect(filter(items, {$: 'dollar'}).length).toBe(1);
expect(filter(items, {$: 'dollar'}, null, '%').length).toBe(0);
expect(filter(items, {'%': 'baz'}).length).toBe(0);
expect(filter(items, {'%': 'baz'}, null, '%').length).toBe(2);
});
it('should match any properties in the nested object for given deep "$" property', function() {
var items = [{person: {name: 'Annet', email: 'annet@example.com'}},
{person: {name: 'Billy', email: 'me@billy.com'}},
@@ -425,6 +444,7 @@ describe('Filter: filter', function() {
toThrowMinErr('filter', 'notarray', 'Expected array but received: {"toString":null,"valueOf":null}');
});
it('should not throw an error if used with an array like object', function() {
function getArguments() {
return arguments;
@@ -439,7 +459,6 @@ describe('Filter: filter', function() {
expect(filter(argsObj, 'i').length).toBe(2);
expect(filter('abc','b').length).toBe(1);
expect(filter(nodeList, nodeFilterPredicate).length).toBe(1);
});
+35 -11
View File
@@ -3,13 +3,13 @@
describe('$httpBackend', function() {
var $backend, $browser, callbacks,
var $backend, $browser, $jsonpCallbacks,
xhr, fakeDocument, callback;
beforeEach(inject(function($injector) {
callbacks = {counter: 0};
$browser = $injector.get('$browser');
fakeDocument = {
$$scripts: [],
createElement: jasmine.createSpy('createElement').and.callFake(function() {
@@ -28,7 +28,27 @@ describe('$httpBackend', function() {
})
}
};
$backend = createHttpBackend($browser, createMockXhr, $browser.defer, callbacks, fakeDocument);
$jsonpCallbacks = {
createCallback: function(url) {
$jsonpCallbacks[url] = function(data) {
$jsonpCallbacks[url].called = true;
$jsonpCallbacks[url].data = data;
};
return url;
},
wasCalled: function(callbackPath) {
return $jsonpCallbacks[callbackPath].called;
},
getResponse: function(callbackPath) {
return $jsonpCallbacks[callbackPath].data;
},
removeCallback: function(callbackPath) {
delete $jsonpCallbacks[callbackPath];
}
};
$backend = createHttpBackend($browser, createMockXhr, $browser.defer, $jsonpCallbacks, fakeDocument);
callback = jasmine.createSpy('done');
}));
@@ -235,7 +255,7 @@ describe('$httpBackend', function() {
it('should call $xhrFactory with method and url', function() {
var mockXhrFactory = jasmine.createSpy('mockXhrFactory').and.callFake(createMockXhr);
$backend = createHttpBackend($browser, mockXhrFactory, $browser.defer, callbacks, fakeDocument);
$backend = createHttpBackend($browser, mockXhrFactory, $browser.defer, $jsonpCallbacks, fakeDocument);
$backend('GET', '/some-url', 'some-data', noop);
expect(mockXhrFactory).toHaveBeenCalledWith('GET', '/some-url');
});
@@ -294,7 +314,7 @@ describe('$httpBackend', function() {
describe('JSONP', function() {
var SCRIPT_URL = /([^\?]*)\?cb=angular\.callbacks\.(.*)/;
var SCRIPT_URL = /([^\?]*)\?cb=(.*)/;
it('should add script tag for JSONP request', function() {
@@ -310,7 +330,7 @@ describe('$httpBackend', function() {
url = script.src.match(SCRIPT_URL);
expect(url[1]).toBe('http://example.org/path');
callbacks[url[2]]('some-data');
$jsonpCallbacks[url[2]]('some-data');
browserTrigger(script, "load");
expect(callback).toHaveBeenCalledOnce();
@@ -318,6 +338,8 @@ describe('$httpBackend', function() {
it('should clean up the callback and remove the script', function() {
spyOn($jsonpCallbacks, 'removeCallback').and.callThrough();
$backend('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, callback);
expect(fakeDocument.$$scripts.length).toBe(1);
@@ -325,10 +347,10 @@ describe('$httpBackend', function() {
var script = fakeDocument.$$scripts.shift(),
callbackId = script.src.match(SCRIPT_URL)[2];
callbacks[callbackId]('some-data');
$jsonpCallbacks[callbackId]('some-data');
browserTrigger(script, "load");
expect(callbacks[callbackId]).toBe(angular.noop);
expect($jsonpCallbacks.removeCallback).toHaveBeenCalledOnceWith(callbackId);
expect(fakeDocument.body.removeChild).toHaveBeenCalledOnceWith(script);
});
@@ -343,7 +365,9 @@ describe('$httpBackend', function() {
});
it('should abort request on timeout and replace callback with noop', function() {
it('should abort request on timeout and remove JSONP callback', function() {
spyOn($jsonpCallbacks, 'removeCallback').and.callThrough();
callback.and.callFake(function(status, response) {
expect(status).toBe(-1);
});
@@ -359,7 +383,7 @@ describe('$httpBackend', function() {
expect(fakeDocument.$$scripts.length).toBe(0);
expect(callback).toHaveBeenCalledOnce();
expect(callbacks[callbackId]).toBe(angular.noop);
expect($jsonpCallbacks.removeCallback).toHaveBeenCalledOnceWith(callbackId);
});

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