Compare commits

..

428 Commits

Author SHA1 Message Date
Pete Bacon Darwin e242e9b595 fix(angular.merge): do not merge __proto__ property
By blocking `__proto__` on deep merging, this commit
prevents the `Object` prototype from being polluted.
2019-11-07 15:40:09 +00:00
Pete Bacon Darwin 33b5c5067e chore(docs): fix linting issue 2018-11-14 09:45:37 +00:00
Pete Bacon Darwin 7a88a34228 chore(package): update protractor & webdriver dependencies 2018-11-14 09:27:05 +00:00
Jason Bedard dc1c180783 fix(input): prevent browsers from autofilling hidden inputs
Autofilling with previous values (which will then be `$interpolate`ed) could lead to XSS or errors
2018-11-14 09:24:39 +00:00
Pete Bacon Darwin 6592693aad chore(docs-app): ensure ToC links contain the path
Without the path the link is always pointing to the
root page, rather than the current page, which means
that copying the link address or opening the page in
a new tab is broken.

Closes #16608
2018-06-22 21:56:50 +01:00
Pete Bacon Darwin dc3955ca63 style(validators): add an arbitrary jsdoc comment to fix closure compiler builds
Closure compiler complains that the factory function for these validator
directives do not have the parameter documented in the preceding jsdoc
comment. These comments are actually describing the directives and not
their factory functions and so have `@param` tags that do not match the
factory function signature.

This change adds a simple empty(ish) jsdoc comment between the
directive's jsdoc comment and the factory function, which silences
the error from the closure compiler. Luckily the ngdoc processing
does not require these jsdoc comments to be attached to any code
items, so that is not affected by this change.
2018-05-15 07:37:02 +01:00
Martin Staffa 9a3737932a chore(package.json): change distTag to "previous_1_6" 2018-05-09 19:43:32 +02:00
George Kalpakas 0ae0887b2f fix(input[date]): correctly parse 2-digit years
When parsing a date string value, AngularJS uses `new Date(year, ...)`
to create a Date object and assign it to the model. In the constructor,
2-digit years map to 1900-1999, so the created Date object has the wrong
value for year.
This commit fixes it, by explicitly using `setFullYear()` to set the
year to the correct value, when necessary.

Fixes #16537

Closes #16539
2018-04-27 18:54:28 +03:00
Martin Staffa 2549313eab docs(CHANGELOG.md): update date for 1.6.10 2018-04-17 18:35:33 +02:00
Martin Staffa 26fccb9943 docs(CHANGELOG.md): add changes for 1.6.10 2018-04-12 18:40:08 +02:00
Peter Bacon Darwin efb822c58d feat($rootScope): allow suspending and resuming watchers on scope
This can be very helpful for external modules that help making the digest
loop faster by ignoring some of the watchers under some circumstance.
Example: https://github.com/shahata/angular-viewport-watch

Thanks to @shahata for the original implementation.

Closes #5301
Closes #16308
2018-04-12 10:35:21 +02:00
Martin Staffa b7d1e0fbdb fix($compile): throw error in $onChanges immediately
This brings it in line with how we throw errors in a digest cycle,
and also avoids throwing an array.

Closes #15578
Closes #16492
2018-04-12 10:29:50 +02:00
Martin Staffa 31ea071f2e docs($compile): add known issues with replace:true
Closes #16523

Related #2573
Related #5695
Related #9837
Related #10612
2018-04-11 17:55:27 +02:00
Martin Staffa abe6acf598 test(input): fix date type tests with max + timezone
Closes #16526
2018-04-10 23:04:31 +02:00
George Kalpakas f04e04e0e6 fix($compile): correctly handle null/undefined href attrs.$set()
Accidentally broken while backporting #14890.

Since #14890, `$$sanitizeUri()` can no longer handle `null`/`undefined` values.
In 1.7.x, there are no such calls.
In 1.6.x, there is still one such calls inside `Attributes.$set()`, so it needs to be adjusted accordingly.

Closes #16520
2018-04-09 10:23:24 +02:00
Martin Staffa 4355dee21d fix(input): allow overriding timezone for date input types
This commit also fixes a bug where part of the Date object
was re-used even after the input was emptied.

Fixes #16181
Closes #13382
Closes #16336
2018-04-06 16:45:44 +02:00
Mohamed amr 2f0ac696cb fix(input): take timezone into account when validating minimum and maximum in date types
Closes #16342
Closes #16390
2018-04-06 16:45:26 +02:00
George Kalpakas da3d96c0ea refactor(urlResolve): return already parsed URLs unchanged
Closes #14890
2018-04-05 10:11:30 +03:00
Georgios Kalpakas bc775759c8 feat($http): support sending XSRF token to whitelisted origins
Normally, the XSRF token will not be set for cross-origin requests.
With this commit, it is possible to whitelist additional origins, so that requests to these origins
will include the XSRF token header.

Fixes #7862
2018-04-05 10:10:45 +03:00
Michał Gołębiowski-Owczarek fa4451d57f docs(jqLite): document that append doesn't work well with a multi-node object
Contrary to jQuery jqLite's append doesn't clone elements so will not work
correctly when invoked on a jqLite object containing more than one DOM node.

Refs #11446
Closes #16517
2018-04-04 09:47:32 +02:00
Michał Gołębiowski-Owczarek a0c55af985 fix(jqLite): use XHTML-compliant HTML as input for jqLite
Fixes #6917
Closes #16518
2018-04-04 09:47:16 +02:00
Michał Gołębiowski-Owczarek d1dd6a3624 chore(*): stop testing on Edge 15 due to its instability
Edge 15 disconnects from Karma frequently causing extreme build instability.
We are testing on Edge 16 & Edge 17 should be released soon anyway.

Closes #16516
2018-03-28 14:46:04 +02:00
Michał Gołębiowski-Owczarek e5eaecc32b tests(ngMock): remove a broken jqLite cache clearing (#16515)
This commit removes the resetting of `angular.element.cache` in some tests;
this was desynchronizing jqLite.cache & the local jqCache variable and since
some parts of the code use one of them and some the other one, it was breaking
JQLite._data. `angular.element.cache` doesn't even exist when jQuery 2+ is used.

Closes #16515
Refs #16512
2018-03-28 13:22:58 +02:00
Giuseppe Scoppino 24ddbafbdf docs(guide/Internet Explorer Compatibility): include warnings for usage of 'disabled' attribute
* docs(guide/Internet Explorer Compatibility): Mention 'disabled' attribute

Setting the 'disabled' attribute on an element that has descendant elements has unexpected behavior in Internet Explorer 11.

* Input elements that are descendants have the text content of the 'placeholder' attribute inserted as the value (and it is not removed when typing in the field).
* Select elements that are descendants are disabled.

To avoid this issue, it is important to not set `disabled` or `ng-disabled` on an element that has descendant form elements. Normally these should only be used on actual form controls so the issue would not manifest.

The issue can also appear if a directive/component is named 'disabled' or takes an attribute named 'disabled' as an input/output attribute, so avoid these.

Closes  #16490
Related #15700
2018-03-19 18:04:17 +01:00
Martin Staffa 980b69dcae feat(minErr): strip error url from error parameters
Related https://github.com/angular/angular.js/issues/14744
2018-03-19 18:04:11 +01:00
Martin Staffa 52e46683bf fix(minErr): update url to https 2018-03-19 18:04:08 +01:00
Martin Staffa 0b0a122888 chore(travis): update iOs test browsers
Closes #16493
2018-03-16 16:49:57 +01:00
Martin Staffa b166f2b34a docs(ngShow/ngHide): add note about flicker when toggling elements
Related to https://github.com/angular/angular.js/issues/14015

Closes #16489
2018-03-16 13:30:58 +02:00
George Kalpakas ac06fb6592 style($interval): fix indentation 2018-03-13 21:25:47 +02:00
George Kalpakas 20dbc1161f style($timeout): fix indentation 2018-03-13 21:25:22 +02:00
George Kalpakas f78268fbf7 docs(input[radio]): explain what happens with same name on multiple inputs
Closes #15009

Closes #16478
2018-03-12 16:32:23 +02:00
Martin Staffa 85286bd863 docs($http): move response object documentation into Usage section 2018-03-07 11:27:09 +01:00
Martin Staffa 1faf7ec30d fix($http): set correct xhrStatus in response when using 'timeout'
This correctly sets "timeout" if a request fails because the timeout
(numerical or $timeout) is exceeded,
and "abort" if the request is aborted by resolving a promise that was passed in.
2018-03-07 11:27:06 +01:00
Martin Staffa 20724da544 chore(docs.angularjs.org): add csp headers for csp example
Fixes #16270
2018-03-01 16:57:59 +01:00
Martin Staffa 3964598bcf chore(docs-app): add dynamic 404 behavior
Adapted from https://github.com/angular/angular/commit/88045a50506adfe32c2f7a213c8e95f46d1e40e1,
https://github.com/angular/angular/commit/c3fb820473d64036ef0dd3d4c004cc7fbc67be75,
and https://github.com/angular/angular/commit/5a624fa1be530a1b3479a4cc7f96e5a20a3d64fb.
2018-03-01 16:57:58 +01:00
Martin Staffa cd700f65f4 chore(docs.angularjs.org): only deploy production index.html as entry file
Previously, we rewrote index.html to index-production.html, but Firebase ignored
this, probably because an exact file match always takes priority. This lead
to the problem thatthe root - angularjs.org - didn't include the angular.js source
files from the CDN
2018-03-01 16:57:56 +01:00
Martin Staffa f36f25f17a chore(docs.angularjs.org): allow crawling of examples, don't deploy e2e test files 2018-03-01 16:57:55 +01:00
Martin Staffa fcc26063c5 docs(tutorial): fix headlines 2018-02-27 17:47:31 +01:00
Frederik Prijck 0b66753488 docs(README): fix incorrect version-support-status link (#16473)
Closes #16472
2018-02-26 15:04:51 +01:00
George Kalpakas b30a925d54 refactor($compile): avoid catastrophic backtracking when parsing bindings
This isn't expected to have any actual impact, since AngularJS is only
intended to be used in the browser (not the server) and for this RegExp
to be exploited by malicious user code the developer would have to have
to give the user rights to execute arbitrary JavaScript code anyway.
Fixing as a general good practice and to avoid encouraging use of a
similar RegExp in other environments where it might actually matter.

Closes #16464
2018-02-21 11:50:39 +02:00
Pete Bacon Darwin fe599dbb72 docs(misc): add version-support-status page (#16460)
Closes #16058
Closes #16458
2018-02-21 08:33:59 +00:00
Pete Bacon Darwin a7dfa7cd83 docs($route): add missing error document 2018-02-20 12:20:14 +00:00
Pete Bacon Darwin a19a17e288 docs($parse): add missing error documents 2018-02-20 12:18:10 +00:00
Michał Gołębiowski-Owczarek b3224671fb docs(*): add CODE_OF_CONDUCT.md
Closes #16456
2018-02-15 14:37:39 +01:00
Martin Staffa 2953e47da4 chore(docs.angularjs.org): allow crawling but not indexing of partials/
The sitemap.xml might also prevent the indexing, as the partials are not
listed.

Related to #16432 
Closes #16457
2018-02-15 11:54:14 +01:00
Martin Staffa 96bee0c659 chore(docs.angularjs.org): allow robots access to js files
Related to #16432
2018-02-13 10:17:04 +01:00
George Kalpakas 5b87647243 chore(travis): re-enable test jobs 2018-02-13 02:20:22 +02:00
George Kalpakas 6f10c2d9d1 chore(travis): fix ROOT_DIR path when build.sh is sourced 2018-02-13 01:52:02 +02:00
George Kalpakas 76882169ba chore(travis): temporarily disable test jobs 2018-02-13 01:39:26 +02:00
George Kalpakas f93c770cc2 chore(docs.angularjs.org): install firebase dependencies before deploying
Firebase is trying to execute our functions code locally in order to
parse the triggers. Install npm dependencies to avoid errors like:

```
Error: Error parsing triggers: Cannot find module 'firebase-functions'
```

Closes #16453
2018-02-13 01:37:37 +02:00
George Kalpakas 06f5069813 chore(docs.angular.js): do not break when deploying
Follow-up to #16451.

Closes #16452
2018-02-12 23:21:23 +02:00
Martin Staffa 8681fce27b chore(docs.angularjs.org): serve snapshots for googlebot
This commit restores serving the plain partials (content) when a docs
page is accessed with ?_escaped_fragment_=.
The Google Ajax Crawler accesses these urls when the page has
`<meta type="fragment" content="!">` is set.

During the migration to Firebase, this was lost, which resulted in Google
dropping the docs almost completely from the index.

We are using a Firebase cloud function to serve the partials. Since
we cannot access the static hosted files from the function, we have to
deploy them as part of the function directory instead, from which they
can be read.

Related to #16432
Related to #16417
2018-02-12 16:09:54 +01:00
Martin Staffa 5a022524a3 chore(deploy): rename deploy folders 2018-02-12 16:09:52 +01:00
Martin Staffa 5acfac894c chore(code.angularjs.org): don't gzip compressed image files 2018-02-12 16:09:51 +01:00
Martin Staffa a879e82a9b chore(eslint): allow ES6 for node scripts 2018-02-12 16:09:49 +01:00
George Kalpakas c03fda012a fix(docs): fix @media breakpoints for small/extra small devices
Previously, our custom styles used `@media` breakpoints for
small/extra small devices that were off-by-one from
[Bootstrap breakpoints](https://getbootstrap.com/docs/3.3/css/#responsive-utilities-classes)
(767px vs 768px). This caused the site to not be displayed correctly on
these exact sizes, which affected for example all iPad devices (whose
screens are exactly 768px wide).

This commit fixes it by making our breakpoints match those of Bootstrap.

Fixes #16448

Closes #16449
2018-02-11 21:24:18 +02:00
Martin Staffa 976e34bab6 test(input): add test for IE composition bug 2018-02-08 23:51:37 +01:00
Jae Ik Lee 9a1b7c9fa1 fix(input): fix composition mode in IE for Korean input
Fixes #6656
Closes #16273
2018-02-08 23:51:34 +01:00
Martin Staffa c33fd13254 fix(browserTrigger): support CompositionEvent 2018-02-08 23:51:31 +01:00
Martin Staffa 336bad182f chore(docs.angularjs.org): deploy sitemap.xml
Closes #16445
2018-02-08 23:43:00 +01:00
Martin Staffa 553ca78406 chore(code.angularjs.org): increase the cache duration
This is already set, but wasn't checked in
2018-02-08 19:00:42 +01:00
frederikprijck 0722ab0ba1 docs(ngClass): add docs regarding animation for ngClassEven and ngClassOdd
Previously, the documentation has no information regarding using
`ngAnimate` together with the `ngClassEven` and `ngClassOdd` directives.

This commit adds the same docs used by the `ngClass` directive to the
`ngClassEven` and `ngClassOdd` docs and adds an extra example for both
`ngClassEven` and `ngClassOdd` that showcases animations.

Closes #15654
2018-02-06 21:59:26 +02:00
Maksim Ryzhikov ee8e05cfaf feat($sanitize): support enhancing elements/attributes white-lists
Fixes #5900
Closes #16326
2018-02-06 16:09:14 +02:00
Martin Staffa 9509feba99 chore(docs.angularjs.org): fix incorrect rewrites 2018-02-05 22:18:03 +01:00
Martin Staffa b0b3a2b6cb chore(docs.angularjs.org): serve xml files (sitemap) 2018-02-05 19:26:19 +01:00
Martin Staffa 3abe5b77b3 chore(docs.angularjs.org): allow robots to access js and css
Otherwise, the google bot cannot execute the JS
2018-02-05 17:53:55 +01:00
Martin Staffa 858807ad5d chore(code.angularjs.org): fix robots.txt
- allow all-versions-data.js in snapshot, which is used by docs.angularjs.org
- disallow access to folders like docs-0.9.2 etc which are used by early versions
2018-02-05 17:53:49 +01:00
Martin Staffa 7436063a7d chore(doc-gen): generate sitemap.xml 2018-02-05 17:53:46 +01:00
Martin Staffa d1bc780304 chore(docs-gen): generate list of versions in correct order
Closes #16419
2018-02-05 16:29:44 +01:00
Martin Staffa 28f273e473 chore(code.angularjs.org): improve output of directory listing 2018-02-05 16:29:06 +01:00
Martin Staffa 1cdc72f3a5 chore(docs.angularjs.org): add robots.txt 2018-02-02 12:31:49 +01:00
Martin Staffa c3c5c5eea2 docs(changelog): add changes for 1.6.9 2018-02-02 11:19:32 +01:00
Peter Bacon Darwin 4cc7701700 docs(*): ensure naming is correct for Angular(JS) versions
This was originally done on the master branch in
commit 03043839d5,
but never back-ported to 1.6.x.

That wasn't a big deal because docs.angularjs.org
served the master snapshot docs. However, now we
serve the 1.6.x snapshot docs, and it looks strange
that the official docs have the wrong branding :).
2018-02-02 11:10:17 +01:00
Dmitriy 5dc07667de feat(input): add drop event support (#16420) 2018-01-28 11:27:43 +00:00
Martin Staffa 212e5132ab chore(docs.angularjs.org): actually fix deployment
When a file is symlinked, relative paths obviously aren't correct anymore.
This error was masked because Travis didn't fail the job when Firebase
couldn't find the public folder.
To fix, we copy the file and adjust the folder path
2018-01-25 19:35:04 +01:00
Martin Staffa 00162655e7 docs(*): fix headings, links, and wordings 2018-01-25 19:31:40 +01:00
Martin Staffa 09f1325065 chore(docs.angularjs.org): fix firebase deploy
Travis looks for the firebase.json in the repo root,
but we moved each firebase project in its own sub-folder.
To fix, we create a symlink before deployment.
2018-01-22 17:19:55 +01:00
Martin Staffa c01dad694d chore(code.angularjs.org): don't use trailing slashes, and don't set headers after response was sent 2018-01-22 16:00:39 +01:00
Martin Staffa c4586513d1 chore(code.angularjs.org): don't gzip the zip file before uploading 2018-01-22 16:00:39 +01:00
Martin Staffa 7a1270cf4b chore(errors.angularjs.org): add project with redirects to docs.angularjs.org 2018-01-22 16:00:39 +01:00
Martin Staffa 559e93c7ce chore(docs.angularjs.org): move project in separate folder
Firebase projects should be in their own folder, because the firebase-tools
search for projects in the parent folder, which makes it more difficult to create
new projects.
2018-01-22 16:00:38 +01:00
Pete Bacon Darwin 9d058de04b fix(ngMessages): prevent memory leak from messages that are never attached
Closes #16389
Closes #16404
Closes #16406
2018-01-17 16:05:07 +01:00
Martin Staffa 1d826e2f1e fix(ngTransclude): remove terminal: true
This was introduced in commit 2adaff083f,
but made obsolete in 41f3269bfb.

Fixes #16411
Closes #16412
2018-01-17 16:05:07 +01:00
Martin Staffa 5789094546 docs(ngRepeat): improve info about tracking
- deduplicate info between docs section and arguments
- don't draw too much attention to track by $index  ...
- ... but highlight its drawbacks
- add example to show how tracking affects collection updates
- clarify duplicates support for specific tracking expressions

Closes #16332
Closes #16334 
Closes #16397
2018-01-17 16:05:07 +01:00
Martin Staffa cf92c33a32 docs(*): clarify module API and dependency injection rules
Closes #16363
Closes #16395
2018-01-17 16:05:07 +01:00
Martin Staffa 5d8f5a8be7 chore(code.angularjs.org): resolve gcs file stream on 'finish'
'response' is called before the data transfer is complete.
2018-01-17 16:05:07 +01:00
Martin Staffa 74a2808487 chore(docs.angularjs.org): copy unminified angular files
e2e tests use the unminified files even in production deployment
2018-01-17 16:05:07 +01:00
Deb Saunders 9ba181b80f docs(guide/External Resources): remove link to deprecated content
Content at URL is deprecated.

Closes #16403
2018-01-17 16:05:07 +01:00
Frederik Prijck 44c16b5b51 docs(guide/Animations): fix minor typos
Closes #16409
2018-01-17 16:05:07 +01:00
Manuel Darveau 70a42fcaee docs(guide/Using $location): fix typo
Closes #16408
2018-01-17 16:05:07 +01:00
Martin Staffa 3bd62d2d59 chore(node): add karma launchers for Edge, IE, and Safari
That makes it easier to run the tests with these browsers locally.

Closes #16407
2018-01-17 16:05:07 +01:00
Peter Bacon Darwin b9ef6585e1 fix($sanitize): sanitize xml:base attributes
On Firefox there is a XSS vulnerability if a malicious attacker
can write into the `xml:base` attribute on an SVG anchor.

Thanks to Masato Kinugawa at Cure23
2018-01-17 16:05:07 +01:00
Martin Staffa e06b4fb62c chore(travis): test on 2 latest Safari versions 2018-01-17 16:05:07 +01:00
Martin Staffa 9204a1a153 test(*): fix tests for Safari 10+
The mocksSpec change is due to the following issue in Safari 10+ strict mode:
In the following code, Safari will not use the name of the enclosing function (testCaller)
in the stack, but rather list the anonymous function that is called to inject:

```
function testCaller() {
  return inject(function() {
    throw new Error();
  });
}
var throwErrorFromInjectCallback = testCaller();
```

Naming the anonymous function allows us to check for it in the test.
2018-01-17 16:05:07 +01:00
Martin Staffa f4d49f3769 docs(guide/Controllers): fix headings; re-order info 2018-01-10 13:01:33 +01:00
Martin Staffa d5c35b44d7 chore(node): update semver
Closes #16396
2018-01-10 12:58:52 +01:00
Martin Staffa 551375c736 docs(vendor): add readme 2018-01-08 13:03:36 +01:00
Martin Staffa 17691790e9 refactor(getVersion.js): remove unused parameters
The parameters have been unused since we moved most of the bower
package dependencies to npm
2018-01-08 13:03:24 +01:00
Michał Gołębiowski-Owczarek e47dead052 chore(*): get rid of Bower in favor of Yarn aliases & checked-in packages
Bower was used to install multiple versions of jQuery which is now handled
using Yarn aliases. The remaining two packages, closure-compiler and
ng-closure-compiler were installed from zip files which is not supported by Yarn
(see https://github.com/yarnpkg/yarn/issues/1483); the first of them exists
on npm as the google-closure-compiler but only versions newer than we used are
published and they don't work with ng-closure-compiler so - instead - both were
checked in to the repository.

Fixes #16268
Fixes #14961
Ref yarnpkg/yarn#1483
2018-01-08 13:03:20 +01:00
Martin Staffa 1a2532601b chore(node): update karma to 2.0.0
Also reduce karma log level on Travis to INFO.
Before, the log level was DEBUG, but it seems that
prior to karma 2.0.0, the debug messages were not outoput on Karma,
so this simply restores the status quo (and prevents cluttering the log).
2018-01-08 10:43:12 +01:00
Martin Staffa cb862aaaa0 chore(node): update jasmine to 2.8.0, and adjust tests 2018-01-08 10:43:11 +01:00
Martin Staffa fa2034c167 test($sanitize): skip nextSibling clobber test on Edge 16
In some circumstances, Edge 16 will not throw on clobbered
elements, because the nextSibling property is null. The exact
cause is currently unknown.
2018-01-08 10:43:10 +01:00
Martin Staffa 750b43b6f6 chore(*): update copyright year
Closes #16386
2018-01-05 21:07:32 +01:00
Martin Staffa 048f5e3ab8 docs(DEVELOPERS.md): improve testing section 2018-01-05 21:07:26 +01:00
Sergey Kryvets 13c566cc03 docs(developers.md): update node version as specified in package.json
Closes #16384
2018-01-05 21:07:19 +01:00
Georgii Dolzhykov 45834eac09 docs(ngModel.NgModelController): correct description for $viewChangeListeners
It was misleading.

Closes #16382
2018-01-05 21:07:13 +01:00
Georgios Kalpakas 3673909896 feat(currencyFilter): trim whitespace around an empty currency symbol
In most locales, this won't make a difference (since they do not have
whitespace around their currency symbols). In locales where there is a
whitespace separating the currency symbol from the number, it makes
sense to also remove such whitespace if the user specified an empty
currency symbol (indicating they just want the number).

Fixes #15018
Closes #15085

Closes #15105
2017-12-19 11:32:05 +02:00
Martin Staffa 8302981a08 docs(CHANGELOG.md): add changes for 1.6.8 2017-12-18 15:17:56 +01:00
Martin Staffa d2a7b5162f docs(*): add browserTrigger docs; update references to scenario runner 2017-12-18 15:06:21 +01:00
Jason Bedard 2ecd85b989 test($rootScope): test recursive event broadcast and emit 2017-12-18 15:06:20 +01:00
Peter Bacon Darwin 2bdf712687 fix($location): always decode special chars in $location.url(value)
The original fix for #16312 included changing how `$location.url(value)`
decoded the special characters passed to it as a setter.
This broke a number of use cases (mostly involving the ui-router).

Further analysis appears to show that we can solve #16312, to prevent
urls being rewritten with decoded values, without modifying the
behaviour of `$location.url`.

This commit reverts changes to `$location.url(value)` so that encoded
chars will once again be decoded and passed to `$location.path(value)`.
In particular it will convert encoded forward slashes, which changes how
the path is updated, since e.g. `a/b/%2Fc%2Fd` will become `a/b/c/d`.
While this is arguably not "correct", it appears that there are too many
use cases relying upon this behaviour.
2017-12-11 19:41:38 +00:00
John Hampton 0de02973c1 docs(ng-model-options): remove extra quotes in example
Remove unnecessary quotes around attribute directive name in the docs example.  This syntax is incorrect.

Closes #16362
2017-12-11 10:57:46 +02:00
Martin Staffa 55516da2df fix(ngModelController): allow $overrideModelOptions to set updateOn
Also adds more docs about "default" events and how to override
ngModelController options.

Closes #16351
Closes #16364
2017-12-08 11:11:42 +01:00
Michał Gołębiowski-Owczarek e8adbf8d5a chore(*): bump Yarn in Jenkins init-node script
Without it Jenkins builds are broken.

Closes #16365
2017-12-07 20:28:02 +01:00
Michał Gołębiowski-Owczarek c169c60535 build(*): update Node from 6 to 8, update Yarn
Angular (2+) switched to Node 8 and so should we.

Closes #16360
Ref angular/angular#20807
Ref angular/angular#20832
2017-12-07 19:27:40 +01:00
Jason Bedard 0b6ec6b3ab refactor($rootScope): consistently use noop as the default $watch listener
Closes #16343
2017-12-07 11:14:29 +01:00
Martin Staffa d2511cfac0 chore(travis): fix deployment condition to include tagged commits
Tagged commits are not considered to belong to any branch.

Closes #16346
2017-12-07 11:13:09 +01:00
jugglinmike 155efa421b docs(ngNonBindable): document effect on the element's directives
The phrase "contents of the current DOM element" may be interpreted either as
inclusive of the DOM element's attributes or as exclusive of the attributes.
This situation concerns markup such as:

    <div ng-non-bindable ng-controller="MyController"></div>

In practice, AngularJS does not compile or bind attribute values for elements
which specify the `ng-non-bindable` directive. Extend the documentation to
definitely describe this behavior.

Closes #16338
2017-12-07 11:13:09 +01:00
Francesco Pipita f33d95cfcf feat($parse): add a hidden interface to retrieve an expression's AST
This PR adds a new private method to the `$parse` service, `$$getAst`,
which takes an Angular expression as its only argument and returns
the computed AST. This feature is not meant to be part of the public
API and might be subject to changes, so use it with caution.

Closes #16253

Closes #16260
2017-11-30 16:33:19 +02:00
Peter Bacon Darwin 57b626a673 fix($location): decode non-component special chars in Hashbang URLS
Fixes https://github.com/angular/angular.js/pull/16316#issuecomment-347527097
2017-11-30 13:23:16 +00:00
Martin Staffa b168aef861 docs(CHANGELOG.md): add changes for 1.6.7 2017-11-24 18:44:04 +01:00
Jason Bedard 6c4d2707b7 refactor($rootScope): simplify $emit stopPropagation handling
See https://github.com/angular/angular.js/pull/16293#discussion_r147960028

Closes #16339
2017-11-24 09:27:31 +01:00
Jason Bedard 9c95f6d5e0 perf(jqLite): avoid setting class attribute when not changed 2017-11-23 18:44:54 +01:00
Jason Bedard cab9ebfd5a perf(jqLite): avoid repeated add/removeAttribute in jqLiteRemoveClass
Fixes #16078
Closes #16131
2017-11-23 18:44:46 +01:00
Michał Gołębiowski-Owczarek 8a739fb4fa chore(*): normalize Vojta's email in .mailmap correctly
Closes #16340
2017-11-22 13:25:58 +01:00
Denys B c4003fd034 fix($compile): sanitize special chars in directive name
This fixes regression bug
when directive name with preceeding special char
in HTML markup does not match the registered name.
(introduced in https://github.com/angular/angular.js/commit/73050cdda04675bfa6705dc841ddbbb6919eb048)

Closes #16314
Closes #16278
2017-11-17 12:39:59 +01:00
kirk ab856d8ae3 docs(linky): mark "target" param as optional
This argument is optional in practice, and it is not provided in many of the examples in the
documentation. Its optional presence is handled here:
https://github.com/angular/angular.js/blob/f876ab71913e17e9126baad19ab795f28b61bfe6/src/ngSanitize/filter/linky.js#L185

Closes #16330
2017-11-17 12:39:52 +01:00
Frederik Prijck 83e6eef68e docs(*): remove usage of global grunt-cli
Previously, the `DEVELOPERS.md` and `CONTRIBUTING.md` files
refered to global `grunt-cli` by default.

This commit ensures the local `grunt-cli` is used by default
and mentiones the possibility to still use the global `grunt-cli`.
2017-11-17 12:39:45 +01:00
Martin Staffa 87270cb79f docs(*): update CONTRIBUTING.md and create DEVELOPERS.md
CONTRIBUTING.md
- focus on basic info about issues and pull requests for new contributors
- move development heavy info to DEVELOPERS.md + add links
- remove outdated info

DEVELOPERS.md
- contains info about project setup, coding rules, and commit message guidelines from CONTRIBUTING.md
- add and update info about writing docs from Wiki
- add info about development setup from docs contribute.md
- add info about running tests on Saucelabs / Browserstack

Closes #7303
Closes #9444
Closes #16297
2017-11-17 12:39:21 +01:00
Peter Bacon Darwin e06ebfdbb5 fix($location): do not decode forward slashes in the path in HTML5 mode
Closes #16312
2017-11-03 11:04:16 +00:00
Peter Bacon Darwin ddeb1df15a fix(sanitizeUri): sanitize URIs that contain IDEOGRAPHIC SPACE chars
Browsers mutate attributes values such as `&#12288;javascript:alert(1)`
when they are written to the DOM via `innerHTML` in various vendor specific
ways.

In Chrome (<62), this mutation removed the preceding "whitespace" resulting
in a value that could end up being executed as JavaScript.

Here is an example of what could happen:
https://plnkr.co/edit/Y6EsbsuDgd18YTn1oARu?p=preview
If you run that in Chrome 61 you will get a dialog box pop up.

There is background here:
http://www.nds.rub.de/media/emma/veroeffentlichungen/2013/12/10/mXSS-CCS13.pdf

The sanitizer has a bit of code that triggers this mutation on an inert piece
of DOM, before we try to sanitize it:
https://github.com/angular/angular.js/blob/817ac567/src/ngSanitize/sanitize.js#L406-L417

Chrome 62 does not appear to mutate this particular string any more, instead
it just leaves the "whitespace" in place. This probably means that Chrome 62
is no longer vulnerable to this specific attack vector; but there may be
other mutating strings that we haven't found, which are vulnerable.

Since we are leaving the mXSS check in place, the sanitizer should still
be immune to any strings that try to utilise this attack vector.

This commit uses `trim()` to remove the IDEOGRAPHIC SPACE "whitespace"
before sanitizing, which allows us to expose this mXSS test to all browsers
rather than just Chrome.

Closes #16288
2017-11-03 10:14:59 +00:00
Jason Bedard 358a69fa8b fix($rootScope): fix potential memory leak when removing scope listeners
When removing listeners they are removed from the array but the array size
is not changed until the event is fired again. If the event is never fired
but listeners are added/removed then the array will continue growing.

By changing the listener removal to `delete` the array entry instead of setting
it to `null` browsers can potentially deallocate the memory for the entry.

Fixes #16135
Closes #16161
2017-10-27 20:51:34 -07:00
Jason Bedard 417aefd45d test($rootScope): test removal of event listeners during event broadcast 2017-10-27 20:51:34 -07:00
Martin Staffa 6ad4c8d1b4 chore(travis): tighten up deploy conditions 2017-10-26 14:30:28 +02:00
Martin Staffa 3ef612afa0 chore(travis): deploy to docs and code when distTag=latest
We now deploy to code.angularjs.org and docs.angularjs.org
when we are on the branch which has distTag=latest set in the
package.json, i.e. the stable branch.

Previously, we deployed to docs only when distTag=latest and
the commit was tagged, and to code only on the master branch.
2017-10-25 18:14:43 +02:00
Martin Staffa cc8486f994 chore(doc-gen, docs-app): generate "stable snapshot" for distTag=latest
The "stable snapshot" is the current state of the branch that has
distTag=latest, i.e. a preview of the next patch version of the stable branch.
2017-10-25 18:14:43 +02:00
Martin Staffa b990fa91cd chore(travis): split unit test jobs into 'core' and 'jquery'
"unit-core" consists of code+jqlite, module test, and promise A+ tests.
"unit-jquery" is code+jquery
"docs-app" includes unit and e2e tests

Splitting the unit tests into more than one job makes it faster
to rerun jobs that fail because Safari or Edge cannot complete the
suite, which seemingly happens on random.

Closes #16292
2017-10-25 18:14:43 +02:00
Sagir Khan 17f7d8c5c8 docs(tutorial/step_14): replace broken web platform docs link
Replace broken [webplatform-animations][1] link with [mdn-animations][2].

The original link returns a 404. The closest match that works is
https://webplatform.github.io/docs/css/properties/animation. However,
the notice at the top of the page reads:

> The WebPlatform project, supported by various stewards between 2012
> and 2015, has been discontinued.

The CSS animations guide on MDN web docs is not only current, but also
more comprehensive.

[1]: https://docs.webplatform.org/wiki/css/properties/animations
[2]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Animations/Using_CSS_animations

Closes #16294
2017-10-23 11:48:11 +03:00
Martin Staffa 1e8dd0e65c chore(travis): remove unnessecary addons
The "Trusty" build environment on Travis includes these addons,
see https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements

Closes  #16286
2017-10-19 17:42:29 +02:00
Martin Staffa b1a18ef381 chore(travis): unit-test latest & latest-1 Chrome, FF, Edge
Previously, we used fixed versions that became outdated quickly for
FF and Chrome.

Safari 10/11 is not included because during the latest test there were 
failures, see https://github.com/angular/angular.js/pull/15717

Jasmine is fixed to 2.5.2 because 2.6.0+ is not compatible with the suite:
https://github.com/angular/angular.js/pull/15927#issuecomment-309206419

Closes #15927
2017-10-19 17:42:25 +02:00
Martin Staffa 3470ab5696 chore(doc-gen): add directive names that aren't params to usage section
When a directive can be used as an attribute or CSS class, but doesn't take 
a value, its name is not included in the parameters, which previously meant 
that the directive name was missing from the Attribute / CSS Class usage 
section of the docs.

This commit adds the name to the Usage section when it is missing
from the parameters.

Closes #14045
Closes #16265
2017-10-19 17:41:24 +02:00
Martin Staffa 5d39a118df chore(node): update karma-sauce-launcher
Related to #14961
Closes #16281
2017-10-17 14:43:44 +02:00
Martin Staffa 421a5856f8 docs(select.SelectController): fix example
Closes #16271
Closes #16275
2017-10-13 18:02:43 +02:00
Martin Staffa 85f40a15fb chore($resource): remove undocumented API
This code has been in the $resource service since 2010, but was
never documented and can therefore be removed. It'll save precious bytes!

Shout-out to @gkalpak for finding this

Closes #16267
2017-10-13 18:02:42 +02:00
Martin Staffa b919a2737e test(docs-app): access provider that is the same in 1.6 and master 2017-10-11 18:40:40 +02:00
Dmitriy a675ea0343 feat($sanitize, $compileProvider, linky): add support for the "sftp" protocol in links
Add support for the sftp protocol in the linky filter and the "aHrefSanitizationWhitelist" that is used by $sanitize and can be configured in the $compileProvider.

Closes #16102
2017-10-11 17:37:56 +02:00
Martin Staffa 8fb8d52664 docs(ngPattern): add option of specifying literal RegExp
Closes #15929
2017-10-11 17:37:53 +02:00
Martin Staffa d60d904747 docs(ngInit): clarify when it's okay to use ngInit
Closes #10489
2017-10-11 17:36:26 +02:00
Martin Staffa 62afb6204c docs(*): add / correct @-tags; fix headlines; add info
- add / correct `@param`, `@restrict`, `@element`, `@priorìty` to directives
- use `@animations` instead of manual headings
- fix more incorrect h1 headings
- fix incorrectly indented `<examples>`
- add some info to $templateCache and $templateRequest
2017-10-11 17:35:58 +02:00
Martin Staffa 1eaf4ab414 docs(*): fix heading levels; use @example instead of headings
This ensures the doc-gen creates correct headlines and
table of contents entries.
2017-10-11 17:34:18 +02:00
Martin Staffa 1de5d181da chore(doc-gen): improve headings for events and examples
The "Events" heading now gets an id (which makes them show in
the table of contents, and their "Parameters" use a heading with
a lower priority (previously it was the same as "Events" itself).

The "@example" tag now generates the heading "Example" if there's
only one, or "Examples" if there are multiple.
2017-10-11 17:34:06 +02:00
Martin Staffa 73088bb7ee refactor(api-pages.scenario.js): remove misleading describe block 2017-10-11 17:33:59 +02:00
Martin Staffa 898140edb7 docs(*): fix heading levels and general clean-up
- docs pages should only have one h1
- docs pages shouldn't skip a h* in the hierarchy
- manual table of contents are no longer necesary
- references to the doc-module-components directive are obsolete
2017-10-11 17:33:34 +02:00
Martin Staffa dab60352e5 chore(docs-app): add table of contents to individual pages 2017-10-11 17:29:13 +02:00
Michał Gołębiowski-Owczarek df4955fe78 chore(*): add a Git .mailmap with my new name
In this way:
* my past contributions are mapped correctly
* other people's distinct entries are collapsed into one

Closes #16254
2017-10-04 23:25:28 +02:00
Martin Staffa 14519488ce feat(ngModel.NgModelController): expose $processModelValue to run model -> view pipeline
Closes #3407
Closes #10764
Closes #16237
2017-09-29 13:13:55 +02:00
Peter Bacon Darwin 569e906a58 fix(http): do not allow encoded callback params in jsonp requests 2017-09-24 14:58:17 +01:00
George Kalpakas 18b8a63a3c chore(doc-gen): fix script paths in plnkr for examples with deps
Closes #16238
2017-09-22 23:30:25 +03:00
Craig Leyshan 6e78fee732 feat($injector): ability to load new modules after bootstrapping
The new method `$injector.loadNewModules(modules)` will add each of the
injectables to the injector and execute all of the config and run blocks
for each module passed to the method.

* The application developer is responsible for loading the code containing
the modules.
* Modules cannot be unloaded.
* Previously loaded modules will not be reloaded.
* Previously compiled HTML will not be affected by newly loaded directives,
filters and components.
2017-09-21 13:54:32 +01:00
ImgBotApp 6a99eaf1c8 docs(*): optimize images
\docs\app\assets\img\AngularJS-small.png (1.82%)
\docs\img\One_Way_Data_Binding.png (12.15%)
\docs\img\guide\di_sequence_final.png (19.05%)
\docs\app\assets\img\bullet.png (13.68%)
\images\docs\Diagrams.svg\image1.png (1.73%)
\docs\img\angular_parts.png (30.41%)
\images\docs\tutorial\tutorial_06.svg\image15.png (1.55%)
\docs\img\Two_Way_Data_Binding.png (11.59%)
\docs\img\guide\concepts-databinding2.png (7.54%)
\images\docs\Diagrams.svg\image4.png (1.89%)
\images\docs\Diagrams.svg\image2.png (1.93%)
\images\docs\Diagrams.graffle\image9.png (2.12%)
\docs\img\guide\concepts-module-injector.png (2.41%)
\docs\img\guide\hero-detail.png (41.79%)
\images\docs\tutorial\tutorial_09.graffle\image9.png (7.86%)
\images\docs\Diagrams.svg\image9.png (2.12%)
\images\docs\guide\concepts.graffle\image4.png (10.43%)
\docs\img\guide\scenario_runner.png (37.15%)
\docs\img\guide\concepts-directive.png (1.05%)
\docs\img\guide\concepts-databinding1.png (8.75%)
\images\docs\guide\concepts.graffle\image1.png (9%)
\images\docs\tutorial\tutorial_09.svg\image9.png (15.35%)
\docs\img\form_data_flow.png (6.68%)
\docs\img\guide\heroes-list.png (40.81%)
\images\docs\guide\concepts.graffle\image5.png (12.02%)
\docs\img\guide\simple_scope_final.png (0.51%)
\docs\img\guide\concepts-startup.png (3.22%)
\docs\img\tutorial\tutorial_12.png (10.49%)
\images\docs\guide\concepts.svg\image4.png (10.43%)
\images\docs\Diagrams.graffle\image4.png (1.89%)
\docs\img\tutorial\tutorial_05.png (11.11%)
\docs\img\tutorial\catalog_screen.png (0.46%)
\images\docs\tutorial\tutorial_10.graffle\image10.png (5.73%)
\images\docs\tutorial\simple_scope.graffle\image7.png (43.81%)
\images\docs\guide\simple_scope.graffle\image7.png (43.81%)
\images\docs\tutorial\tutorial_02.graffle\image11.png (0.19%)
\docs\img\guide\crisis-list.png (39.68%)
\docs\img\guide\concepts-runtime.png (2.48%)
\images\docs\guide\concepts.svg\image5.png (12.02%)
\docs\img\guide\concepts-view.png (2.98%)
\images\docs\tutorial\tutorial_02.svg\image11.png (28.1%)
\docs\img\tutorial\tutorial_00.png (10.99%)
\docs\img\tutorial\tutorial_06.png (11.3%)
\docs\img\guide\crisis-detail.png (38.86%)
\docs\img\guide\concepts-scope-watch-strategies.png (7.59%)
\images\docs\guide\simple_scope.svg\image7.png (43.81%)
\docs\img\tutorial\tutorial_09.png (10.7%)
\images\docs\tutorial\tutorial_12.svg\image10.png (25.88%)
\images\docs\tutorial\tutorial_10.svg\image10.png (25.88%)
\images\logo\AngularJS.exports\AngularJS-large.png (3.86%)
\images\docs\Diagrams.graffle\image1.png (1.73%)
\images\docs\tutorial\tutorial_03.svg\image11.png (28.1%)
\docs\img\tutorial\tutorial_10.png (10.44%)
\images\docs\tutorial\tutorial_proto.graffle\image7.png (43.81%)
\images\docs\tutorial\simple_scope.svg\image7.png (43.81%)
\images\logo\AngularJS.exports\AngularJS-small.png (3.64%)
\images\docs\tutorial\tutorial_proto.svg\image7.png (43.81%)
\docs\img\tutorial\tutorial_02.png (10.91%)
\images\docs\tutorial\tutorial_05.graffle\image13.png (10.92%)
\images\logo\AngularJS-Shield.exports\AngularJS-Shield-large.png (5.3%)
\docs\img\tutorial\tutorial_03.png (10.77%)
\images\docs\tutorial\tutorial_05.svg\image13.png (3.46%)
\images\logo\AngularJS.graffle\image1.png (0.55%)
\images\docs\Diagrams.graffle\image2.png (1.93%)
\images\docs\tutorial\tutorial_12.graffle\image10.png (5.73%)
\images\logo\AngularJS-Shield.exports\AngularJS-Shield-huge.png (2.99%)
\images\docs\tutorial\tutorial_06.graffle\image15.png (16.51%)
\images\logo\AngularJS.graffle\image2.png (5.21%)
\docs\img\guide\hashbang_vs_regular_url.jpg (32.92%)
\images\logo\AngularJS-Shield.exports\AngularJS-Shield-medium.png (6.18%)
\images\logo\AngularJS-Shield.exports\AngularJS-Shield-small.png (6.58%)
\images\logo\AngularJS.exports\AngularJS-medium.png (3.37%)
\images\logo\AngularJS.exports\AngularJS-huge.png (3.56%)

Closes #16222
2017-09-21 13:48:52 +01:00
Michał Gołębiowski-Owczarek c94e44b9f8 docs(*): don't hide results for middle-clicks
Middle-clicking opens a link in a new tab; it shouldn't close the results list
as the user may want to open more of those links.

Closes #16233
2017-09-21 14:31:48 +02:00
Michał Gołębiowski-Owczarek bbca6b0893 chore(jqLite): fix $destroy describe block titles 2017-09-20 14:03:04 +02:00
Marcin Wosinek 1555a4911a fix(ngMock): pass unexpected request failures in $httpBackend to the error handler
Closes #16150
Closes #15855
2017-09-18 21:31:20 +01:00
Fengwei Wang dbba98b9ae docs(guide/bootstrap): fix typo ('There a few things' --> 'There are a few things')
Closes #16221
2017-09-11 14:37:15 +03:00
melbourne2991 3e2cb6c15c docs(input[checkbox]): fix @param type for ngChange
Closes #16219
2017-09-07 15:49:49 +03:00
Martin Staffa 1391e99c7f fix(ngAnimate): don't close transitions when child transitions close
Previously, non-angular CSS transitions / animations on child elements
would trigger the ngAnimate [transition|animation]end listener and
could close a running animation prematurely.

Closes #16210
2017-09-06 16:22:13 +02:00
Martin Staffa 7a5f06d55d fix(ngMock.browserTrigger): add 'bubbles' to Transition/Animation Event
When the event objects are created synthetically, the bubbles property is set to false
if not explicitly defined
2017-09-06 16:22:07 +02:00
Yonatan Kra bf78beeb6d docs(guide/expression): add clarification for RegExp literal in ngPattern expression
The `ngPattern` expression does accept a RegExp created with literal notation,
hence it should be mentioned as an exception to the
"No RegExp Creation With Literal Notation" rule.

Closes #16206
2017-09-05 15:55:48 +03:00
frederikprijck 2e892e4072 docs(*): replace master/slave with leader/follower
Previously, the docs made use of `master/slave`, which is offensive.

This commit removes the usage of these terms and replace them with `leader/follower`.
2017-08-28 10:11:40 +01:00
Martin Staffa 8f88ea57dc chore(docs.angularjs.org): fix docs deploy for source file changes
Since the embedded examples in the docs app now include the local source files instead
of CDN files, we need to include the source files into the folder that is deployed
to Firebase hosting
2017-08-24 11:02:17 +02:00
Martin Staffa 24b0b516b6 chore(doc-gen): include source files from code.angularjs.org for production plnkr examples 2017-08-24 11:02:17 +02:00
Martin Staffa 1fcf5949d2 revert: chore(docs-app): load example files based on active deployment
This reverts commit d91a6bdbc6.

The runnable examples cannot rely on the CDN for loading the common files,
because the CDN push does not happen at the same time as the docs are generated,
which means the doc examples are non-functional for an unforeseeable time.
2017-08-24 11:02:17 +02:00
Oscar Busk 46ccaee9be docs($compileProvider): add more info on debugInfoEnabled()
Add more info on what `debugInfoEnabled()` affects.

Closes #16154
2017-08-23 22:11:37 +03:00
Oscar Busk 86c9990813 docs(guide/production): add more info on debugInfoEnabled()
Add bullet mentioning how placeholder comments are affected.

Closes #16155
2017-08-23 19:00:32 +03:00
Martin Staffa 4a34c8e1b8 docs(CHANGELOG.md): add changes for 1.6.6 2017-08-18 15:12:44 +02:00
Martin Staffa fec2ea8c1b chore(karma): use dots reporter by default
dots is nicer for local testing, as you usually don't run a
ton of test suites, and the progress visualization is more important .
It's also more readable if you skip many tests (i.e. run isolated tests)

Closes #16167
2017-08-18 15:08:22 +02:00
Martin Staffa 7566c7d26f chore(code.angularjs): delete old zip files on snapshot 2017-08-15 10:59:19 +02:00
Martin Staffa 02a19c0f39 chore(code.angularjs): enable directory listings 2017-08-15 10:59:19 +02:00
Martin Staffa 6e6e4eec9d chore(travis): skip build on deployment job when from Pull Request 2017-08-15 10:59:19 +02:00
Martin Staffa 46f14fa4b8 chore(docs-app): only copy relevant assets
This keeps the size of the docs-app build down.
Especially needed to keep the size of the generated build .zip
under 10MB, which is the limit for firebase / gcs https function transfers
2017-08-15 10:59:19 +02:00
Martin Staffa bad2249bcd chore(docs-app): load example files based on active deployment 2017-08-15 10:59:19 +02:00
Georgios Kalpakas c2148ec15c test(ngOptions): fix flaky test on Firefox 54+ and Safari 9
Closes #16149
2017-08-12 12:06:11 +03:00
Martin Staffa 474e40498e chore(travis): manually install yarn
Currently, the yarn version on Travis is outdated:
https://github.com/travis-ci/packer-templates/issues/478
2017-08-11 16:38:15 +02:00
Frederik Prijck e9a6792d7f docs(*): change Angular to AngularJS
Closes #16163
2017-08-11 16:57:43 +03:00
Frederik Prijck adc1501caa docs($compile): update preAssignBindingsEnabled description
Previously, the changelog and migration guide did not mention the fact
that this only applies to controllers which are part of a
directive/component.

This commit ensures the changelog and migration guide mentions this explicitly.

Closes #15740

Closes #16162
2017-08-11 16:26:31 +03:00
Georgios Kalpakas 7a53707c8f chore(karma): run tests on latest Chrome (59) and Firefox (54) available on Saucelabs
Closes #16141
2017-08-04 12:10:31 +03:00
Georgios Kalpakas 57fe5b320d test(*): fix tests involving submit on Chrome 60
On Chrome 60 (at least on Windows) the `submit` event when clicking on a submit
button is not fired on the form element, unless it is already part of the DOM.
2017-08-04 12:10:06 +03:00
andypotts 671bebde0a docs(guide/concepts): simplify currency exchange API example (YQL --> Fixer.io)
Fixes #16130

Closes #16137
2017-08-02 13:34:41 +03:00
Carl b51ded6736 fix($httpParamSerializer): ignore functions
Closes #16133
2017-07-31 16:15:27 +03:00
Zita Nemeckova 3ec1819b91 feat($compile): add strictComponentBindingsEnabled() method
Closes #16129
2017-07-28 13:54:31 +03:00
Chirag Bhatia 009ebec64c fix($resource): do not throw when calling old $cancelRequest()
Closes #16037
2017-07-27 10:40:04 +03:00
Kerry McCullough 9256dbc420 feat($resource): add resource to response for error interceptors
Closes #16109
2017-07-18 17:12:11 +03:00
Jason Bedard 7504656a26 fix($parse): do not shallow-watch computed property keys
Shallow watching is not enough when an object implements a non-pure toString
2017-07-18 00:21:42 -07:00
Jason Bedard e74cdf4b59 refactor($parse): remove unnecessary .constant if when collecting inputs 2017-07-18 00:21:42 -07:00
Jason Bedard 9d6c3f3ec2 fix($parse): support constants in computed keys 2017-07-18 00:21:42 -07:00
Martin Staffa ac0e260765 test(ngTouch.ngClick): exclude failing Edge test 2017-07-14 12:57:55 +02:00
Martin Staffa 28c0213ee5 test(core): expect that Edge cannot auto-bootstrap in extensions 2017-07-14 12:57:55 +02:00
Martin Staffa 8b4d85c015 chore(*): deploy (docs|code) .angularjs.org to Firebase via Travis
- code.angularjs.org and docs.angularjs.org are two separate Firebase projects
- both are automatically deployed via Travis config
- Travis is split up into 2 build stages: first, all tests are run, and if they pass, the deploy
stage runs a single job with both deployments (actual deployment depends on the state of the commit)
- docs. is deployed directly to Firebase hosting
- code. is uploaded to Firebase Google Cloud Storage and uses Firebase hosting rewrites to acces the
files
- jenkins builds still push the code builds to the code.angularjs.org Github repository

Closes #9674
Closes #16093
2017-07-14 12:57:55 +02:00
Martin Staffa 2aeda67909 test(input): exclude tests that are only failing on Edge 2017-07-14 12:57:55 +02:00
Martin Staffa ad68ee192e chore(travis): test on Microsoft Edge 2017-07-14 12:57:55 +02:00
Martin Staffa 970ba117eb chore(grunt): increase task readability 2017-07-14 12:57:55 +02:00
Martin Staffa 30e097b389 chore(travis): use "karma-spec-reporter"
dots reporter creates lots of empty space that makes it tedious to scroll
through the logs. "Spec" is configured to only report on failures.
2017-07-14 12:57:55 +02:00
Martin Staffa e8e02b8bce chore(travis): add commitplease validation to ci-checks
This will provide feedback to contributors without getting in the way of writing invalid commit
messages locally.

The git hook integration is turned off.

Committers who push directly to the repo can be expected to use correct commit messages ;)
Most changes go through PRs anyway.

Note that "Merge commit" messages and everything starting with "WIP" is always allowed
by commitplease. Follow issue https://github.com/jzaefferer/commitplease/issues/101 for more info.

Related to #14888
Closes #16097
2017-07-14 12:57:55 +02:00
Martin Staffa e36a3e89f5 chore(travis): fix bower install
We removed `grunt package` from JOB_UNIT in 4015e0fde5,
but this runs `grunt bower` which JOB_UNIT needs.

This commit adds `grunt bower` to JOB_UNIT.

Closes #16105
2017-07-14 12:57:55 +02:00
Martin Staffa 1102c41196 chore(travis): don't build package for the "unit" job
Source test:unit do not need built or packaged code,
and, and tests:docs only need built code.
2017-07-14 12:57:55 +02:00
Martin Staffa e970c8fce9 chore(doc-gen): insert current tagged version if missing from list of all versions
In commit ce49edc08b, we switched to npm info (now yarn info)
instead of the local git repository information to get the list of currently available versions for
the docs app. This means that during a release the version that is currently tagged is not yet
available on npm, and therefore our list of available versions is incomplete.

We now simply add the current build version (read from build/version.json) to the list of all
versions if it fulfills the following conditions:

- it is not a snapshot build
- it is not already part of the list of all versions (i.e. if you are building locally on a tagged commit)

Closes #15741
Closes #16099
2017-07-12 11:34:34 +02:00
Martin Staffa 04271d6b2c docs(ngRepeat): improve track by note 2017-07-12 11:33:50 +02:00
Nikos Katsos 2e1163ef5c fix($http): do not throw error if Content-Type is not application/json but response is JSON-like
Previously, when the response data was JSON-like, `$http` would try to
`JSON.parse` them and throw if they were not actually JSON. This happened even
if the `Content-Type` header was not `application/json`. As a result, it was not
possible to send `text/plain` data that looked like JSON (e.g. `{abcd}`).
The reason for not relying solely on the `Content-Type` is that many users serve
JSON data without proper headers.

This commit fixes it by returning the raw response text if `$http` fails to
parse a JSON-like response, unless the `Content-Type` header has been explicitly
set to `application/json` (in which case it will still fail with an error).

Fixes #16027

Closes #16075
2017-07-11 18:51:33 +03:00
Eyal Ronel 75befe723a docs($resource): add missing closing square bracket in example
Closes #16090
2017-07-11 09:49:47 +03:00
Frederik Prijck 5e2bc5bbf3 feat($http): allow differentiation between XHR completion, error, abort, timeout
Previously, it wasn't possible to tell if an `$http`-initiated XMLHttpRequest
was completed normally or with an error or it was aborted or timed out.
This commit adds a new property on the `response` object (`xhrStatus`) which
allows to defferentiate between the possible statuses.

Fixes #15924

Closes #15847
2017-07-04 15:25:12 +03:00
Martin Staffa 079c485b92 docs(ngOpen): improve example, correct browser compat note
Firefox supports details since version 49
2017-07-04 12:32:46 +02:00
Peter Bacon Darwin 19a0c9324c chore(jenkins): do not publish to code.angularjs.org snapshot
While the firewall continues to block the update ports
we will not try to publish there. This will be fixed when we move to hosting
the sites on Firebase.

This means that successful builds on master will not automatically update
code.angularjs.org, this will affect:

* https://code.angularjs.org/snapshot, which people often use to check latest features
* https://docs.angularjs.org, which is supposed to display the docs for the latest master

As it turns out we can manually partially trigger an update by browsing to
https://code.angularjs.org/gitFetchSite.php but we just can’t guarantee that we will update
both the round robin servers.
2017-07-03 22:34:52 +03:00
Georgios Kalpakas 9ef02e72ab docs(changelog): add release notes for 1.6.5 2017-07-03 15:19:52 +03:00
ksvitkovsky 2101126ce7 feat($compile): overload .component() to accept object map of components
Register multiple components with single call as it is possible with other module units.

Closes #14579 
Closes #16062
2017-07-03 11:10:07 +02:00
Jason Bedard a222d0b452 fix($timeout/$interval): do not trigger a digest on cancel
Previously, `.catch(noop)` was used on a rejected timeout/interval to prevent an unhandled rejection error (introduced in #c9dffde1cb). However this would schedule a deferred task to run the `noop`. If the cancelling was outside a digest this could cause a new digest such as with the ng-model `debounce` option.

For unit testing, this means that it's no longer necessary to use `$timeout.flush()` when a `$timeout` has been cancelled outside of a digest. Previously, this was necessary to execute the deferred task added by `.catch(noop).
There's an example of such a change in this commit's changeset in the file `/test/ngAnimate/animateCssSpec.js`.

Fixes #16057
Closes #16064
2017-07-03 11:10:00 +02:00
davesidious 1b8eb231c9 docs($log): add note about blackboxing
Add browser-agnostic hint about blackboxing and the benefits it brings developers when using $log.

Closes #15592
2017-06-30 12:04:21 +02:00
Martin Staffa 06baf1869b docs($rootScope.Scope): clarify $watchGroup "oldValues" argument
This should help to prevent issues such as #8671, #12452, #16004.

Note that the behavior will change in 1.7 (see https://github.com/angular/angular.js/pull/15854)

Closes #12643
Closes #16005
2017-06-29 18:18:32 +02:00
Martin Staffa 82597fc12b test(ngOptions): ensure options are only painted once on compile 2017-06-29 10:54:57 +02:00
Pol Bonastre ff52b188a7 perf(ngOptions): prevent initial options repainting
Avoid double execution of `updateOptions()` method,
which causes a complete repainting of all `<option>` elements.

Fixes #15801
Closes #15812
Close #16071
2017-06-29 10:54:53 +02:00
Martin Staffa dc41f465ba fix(Angular): deprecate angular.merge
This function has problems with special object types but since it's not used in core,
it is not worth implementing fixes for these cases.
A general purpose library like lodash (provides `merge`) should be used instead.

Closes #12653
Closes #14941
Closes #15180
Closes #15992
Closes #16036
2017-06-29 10:53:40 +02:00
Jason Bedard 6e3b5a57cd fix($parse): do not shallow-watch inputs to one-time intercepted expressions 2017-06-19 21:27:31 -07:00
Jason Bedard f003d93a3d Revert "fix($parse): standardize one-time literal vs non-literal and interceptors"
This reverts commit 60394a9d91.
2017-06-19 19:20:43 -07:00
Jason Bedard aac5623247 fix($parse): do not shallow-watch inputs when wrapped in an interceptor fn
Fixes #15905
2017-06-12 21:32:14 -07:00
Saurav Bhattacharya aa03812fd0 docs(external-resources.ngdoc): fix broken link
Closes #16042
2017-06-12 15:04:15 +03:00
Georgios Kalpakas bcb6a494de test(ngMock): fix Firefox craches on Travis
This test keeps causing Firefox 47 (currently used on Travis) to crash and fail
the build. The test passes locally (on Firefox 53). Lowering the loop count from
1000 to 100 seems to fix the issue.
(Note: The crach only affects the mocked implementation of `$interval` and does
not happen locally.)

Closes #16040
2017-06-12 14:45:27 +03:00
Lucio Martinez a1e3f8728e fix(ngMock/$interval): add support for zero-delay intervals in tests
Previously, trying to test code thaat contained zero-delay intervals (e.g.
`$interval(fn, 0)` or `$interval(fn)`) would result in an infinite loop.
This commit avoids the infinite loop, by treating zero-delay intervals as one
second intervals (except for the initial trigger, where they can also be
executed with `$interval.flush(0)`).

Fixes #15952

Closes #15953
2017-06-06 13:36:08 +03:00
Georgios Kalpakas 9e8e3e187d chore(i18n): update CLDR to v30.0.1
Fixes #15976

Closes #15997
2017-06-06 13:17:08 +03:00
Georgios Kalpakas c6554433cf chore(i18n): fix parser for currency patterns without fraction digits
Previously, it was assumed that all currency pattern would have fraction digits.
However, in [closure-library@b9155d5][1] the `agq_CM` locale was modified to
have such a pattern (namely `#,##0\u00A4`).
This commit modifies the parser implementation to account for pattern without a
decimal point (and thus no fraction digits).

[1]: https://github.com/google/closure-library/commit/b9155d5966a228cb33f367c30c275c833b30e3ff#diff-02793124214ad0470ccea6f86b90d786R711
2017-06-06 13:17:07 +03:00
Georgios Kalpakas 43d2a75f4e chore(i18n): fix relative paths in scripts 2017-06-06 13:17:07 +03:00
Georgios Kalpakas 37d2b50812 test(i18n): remove bad assertion 2017-06-06 13:17:06 +03:00
Peter Bacon Darwin 8f31f1ff43 fix($sanitize): use appropriate inert document strategy for Firefox and Safari
Both Firefox and Safari are vulnerable to XSS if we use an inert document
created via `document.implementation.createHTMLDocument()`.

Now we check for those vulnerabilities and then use a DOMParser or XHR
strategy if needed.

Thanks to @cure53 for the heads up on this issue.
2017-06-05 21:03:55 +01:00
Michał Gołębiowski cf84fcf544 chore(package.json): backport package.json/yarn.lock changes from master 2017-06-05 11:33:28 +02:00
Michał Gołębiowski f63bc3cfde test(support): verify support tests results in all tested browsers (#16008)
Closes #16008
2017-05-30 20:21:15 +02:00
Jason Bedard a77943110e test($parse): reorganize $parse tests 2017-05-24 21:49:21 -07:00
Jason Bedard ec97686f2f fix($parse): always re-evaluate filters within literals when an input is an object
Fixes #15964
Closes #15990
2017-05-24 21:49:12 -07:00
Michał Gołębiowski 4ff7b7aa48 chore(testabilityPatch): fix a typo 2017-05-24 10:25:37 +02:00
Michał Gołębiowski 57b837bd5c test($log): run all $log tests in IE9 & non-IE9 logging mode (#15995)
In IE 9 console methods don't inherit from Function.prototype and, hence, don't
have apply. Until recently IE 9 logging in AngularJS was restricted to the
first 2 parameters but that changed as we could just reuse
Function.prototype.apply everywhere, creating one code path for all browsers.
Therefore, we can now run all tests in modes where apply exists on logging
methods and where it doesn't.

Ref #15911
Ref b277e3ead7
Closes #15995
2017-05-24 10:15:21 +02:00
Thomas Grainger 6daca023e4 fix(*): correctly detect Error instances from different contexts
Previously, errors thrown from different contexts (such as an iframe or
webworker) were not detected as `Error` instances and handled accordingly.
This commit fixes it by introducing an `isError()` helper, that is able to
correctly detect such instances.

Fixes #15868

Closes #15872
2017-05-22 14:09:52 +03:00
Sercan Eraslan 91b4eb0f69 docs(guide/component): remove redundant empty controller from example
There is no need for empty controller functions on components, since this is the
default.

Closes #16003
2017-05-22 11:51:40 +03:00
BobChao87 e50ed4da9e fix(orderBy): guarantee stable sort
If a user-provided comparator fails to differentiate between two items, fall
back to the built-in comparator (using the tie-breaker predicate).

Fixes #14881

Closes #15914
2017-05-19 14:41:41 +03:00
Georgios Kalpakas fe5dd1da8f docs(*): fix dangling links
Closes #15984
2017-05-12 18:13:10 +03:00
Georgios Kalpakas df4c03fa33 docs(form): improve the docs for FormController.$setValidity()
Fixes #15963
2017-05-12 18:13:10 +03:00
tsclaus 8d4e626326 docs(ngRepeat): fix argument name in comment to match actual argument (element --> clone)
Closes #15975
2017-05-11 11:41:28 +03:00
Jake Danforth 92aef5d456 docs(error/badrestrict): fix typo (of --> or)
Closes #15979
2017-05-11 11:35:51 +03:00
Michał Gołębiowski eed13cf732 test(*): run class-related tests everywhere; fix eval syntax
1. Wrap an evaled class definition in parens; previously they weren't; the test
   wasn't failing only because it was disabled everywhere outside of Chrome
   and Chrome <59 incorrectly accepted such input.
2. There's no reason to restrict class-related tests just to Chrome; now they
   run in every browser that supports ES6 classes. The classes support test
   was modified to check not only if a class definition parses but also if
   it stringifies correctly which is required by AngularJS. This restriction
   disables class-related tests in current Firefox (53) but will work in v55
   or newer.

Closes #15967
2017-05-09 15:12:00 +02:00
musclor e90200b4de docs(guide/component): remove redundant unit test
Fixes #15968
Closes #15974
2017-05-09 10:20:07 +03:00
Michał Gołębiowski 7f36ba77a0 chore(*): update all Karma-related packages except Karma
The updated karma-chrome-launcher adds support for ChromeHeadless &
ChromeCanaryHeadless launchers; test with:

    karma start karma-jqlite.conf.js --browsers=ChromeCanaryHeadless

The updated karma-firefox-launcher disables multi-process which may increase
stability on Jenkins.

Closes #15966
2017-05-06 17:52:19 +02:00
Valentin 457c58827b docs(guide/templates): add missing closing <script> tag
Closes #15961
2017-05-04 14:20:34 +03:00
Leonardo Souza aae768611f docs(guide/interpolation): fix typo in markdown (code highlight)
Closes #15935
2017-05-02 15:42:32 +03:00
Georgios Kalpakas ce5ffbf667 perf(animate): avoid unnecessary computations if animations are globally disabled
Closes #14914
2017-05-02 15:39:44 +03:00
Georgios Kalpakas ab114af850 feat($animate): add support for customFilter
This commit adds a new `customFilter()` function on `$animateProvider` (similar
to `classNameFilter()`), which can be used to filter animations (i.e. decide
whether they are allowed or not), based on the return value of a custom filter
function.
This allows to easily create arbitrarily complex rules for filtering animations,
such as allowing specific events only, or enabling animations on specific
subtrees of the DOM, etc.

Fixes #14891
2017-05-02 15:39:11 +03:00
Georgios Kalpakas 2759788737 perf($animate): do not retrieve className unless classNameFilter is used 2017-05-02 15:38:27 +03:00
Georgios Kalpakas c643323c17 docs(guide/animations): list missing animated directives (and other improvements)
- List missing animation-aware directives.
- Fix/Improve wording/formatting.
- Fix typos.
- Limit lines to 100 chars.
2017-05-02 15:38:04 +03:00
Dimitri Tarasiuk 9b97a033b0 docs($q): fix typo in qFactory documentation
Closes #15946
2017-04-28 12:22:00 +03:00
Georgios Kalpakas a3226d01fa fix(angular-loader): do not depend on "closure" globals that may not be available
Code that is distributed as part of both `angular.js` and `angular-loader.js`
should not depend on "closure" globals that may not be available in
`angular-loader`.

Fixes #15880

Closes #15881
2017-04-27 22:47:11 +03:00
Martin Staffa 510d0f946f fix(ngOptions): re-render after empty option has been removed 2017-04-27 21:40:59 +02:00
Martin Staffa 0b962d4881 feat(select): expose info about selection state in controller
This allows custom directives to manipulate the select's and
ngModel's behavior based on the state of the unknown and
the empty option.

Closes #13172
Closes #10127
2017-04-27 21:40:59 +02:00
Pol Bonastre 71b4daa4e1 fix(ngOptions): allow empty option to be removed and re-added
This bug was reported as part of angular/angular.js#15801
2017-04-27 21:40:58 +02:00
Martin Staffa e0b02a5040 docs(select): add known issue about Firefox selection behavior
Related to #9134
2017-04-27 21:40:58 +02:00
Martin Staffa a0dd9b0fdd refactor(select, ngOptions): extract common methods; make consistent 2017-04-27 21:40:32 +02:00
Martin Staffa 498bef199a chore(matchers): improve output for toBeMarkedAsSelected 2017-04-27 21:40:32 +02:00
Martin Staffa 17d34b7a98 fix(ngOptions): select unknown option if unmatched model does not match empty option
When a regular / ngOptions select has an explicit *empty* option, this option can be selected
by the user and will set the model to `null`. It is also selected when the model is set to
`null` or `undefined`.

When the model is set to a value that does not match any option value, and is also not
`null` or `undefined`, the *unknown* option is inserted and selected - this is an explicit marker
that the select is in an invalid / unknown state, which is different from an allowed empty state.

Previously, regular selects followed this logic, whereas ngOptions selects selected the empty
option in the case described above.

This patch makes the behavior consistent between regular / ngOptions select - the latter will now
insert and select the unknown option. The order of the options has been fixed to unknown -> empty
-> actual options.
2017-04-27 21:40:32 +02:00
Martin Staffa 72359fd097 test(select, ngOptions): add more tests for "required" with "empty" or "unknown" option 2017-04-27 21:40:32 +02:00
Georgios Kalpakas 72882190f2 chore(docs-app): fix vertical scrolling offset after recent re-design
Previously, the `yOffset` pointed to the `<header>` element, which had a height
of 0 (since all its children have fixed positions). This caused scrolled items
to have 0 vertical offset, essentially hiding the top part behind the static
`<nav>` items.
This commit fixes the vertical scrolling offset, by setting `yOffset` to the
last (lowest) `<nav>` item.

Closes #15945
2017-04-27 19:40:08 +03:00
Michał Gołębiowski fd2d8a5755 chore(*): make package.json & yarn.lock identical to master 2017-04-25 22:11:37 +02:00
Michał Gołębiowski fbe84f95a1 chore(browserstack): Update browserstacktunnel-wrapper, fix options
Only the latest version of the package works correctly (the backend for it at
BrowserStack is not versioned) and the options have changed in the new version
of the package.

Also, iOS 8.0 is no longer available on BrowserStack, only 8.3 is. Instead,
this commit changes it to 9.3 as we shouldn't be testing on 8 anymore anyway.

(a late cherry-pick of ad0bb83819)

Closes #15892
2017-04-25 21:46:01 +02:00
Michał Gołębiowski 3671a43be4 feat($log): log all parameters in IE 9, not just the first two.
IE 9 lacks apply on console methods but it's possible to borrow the apply
method from Function.prototype.
2017-04-25 21:38:30 +02:00
Michał Gołębiowski f6a1ad528d refactor(*): remove workarounds for IE <9, update IE/Edge-related comments 2017-04-25 21:34:26 +02:00
Jacob Hansson d9128e7b23 feat(ngMock): describe unflushed http requests
The current implementation of $httpBackend.verifyNoOutstandingRequest
gives an integer number describing how many requests are unflushed.

While it's superficially easy to solve test errors from that message
by simply adding an additional $httpBackend.flush(), if a developer
is truly not expecting the code to make further requests this is
not ideal.

This change explicitly prints out which additional requests remain
unflushed in the error message, helping her determine if the code
needs changing, or if an additional flush is appropriate.

Before this change:

    Unflushed requests: 1

After this change:

    Unflushed requests: 1
      GET /some

Closes #10596
Closes #15928
2017-04-21 13:49:05 +02:00
TheHalcyonSavant 0b54c1d4a9 docs(guide/migration): remove duplicate entry for commit 13c252
Closes #15919
2017-04-21 13:48:49 +02:00
Martin Staffa 608d623b55 docs($http): correct and clarify default transforms
- baddata error described incorrect http behavior, and workarounds
- httpProvider defaults were missing transformResponse / transformRequest
- http was not clear about JSON detection strategy

Closes #15897 
Closes #15906
2017-04-21 13:48:49 +02:00
Eli Sadoff 9f30bb5475 docs(filter/uppercase): add an example
I saw that the uppercase filter had no example so I decided to add a minimal example to explain how the uppercase filter works.

Thank you very much to @narretz for helping me through this process.

Closes #15885
2017-04-21 13:48:08 +02:00
Martin Staffa 182fb18f00 docs(filter/filter): remove duplicate 'the'
Closes #15893
2017-04-21 13:48:02 +02:00
Atef Ben Ali 3a9fdceeee docs(guide/directive): delete redundant 'the'
Closes #15891
2017-04-21 13:47:55 +02:00
Atul Shimpi ee4ac72170 docs(README): improve vocabulary and orthography
Closes #15876
Closes #15875
2017-04-21 13:47:48 +02:00
michaelb958 eb9fc571a0 docs(guide/i18n): fix links to CLDR
The old link target is dead, deceased, pushing up daisies. I quote:
> The cldr-tmp repository is no longer available.
> For access to CLDR sources and data, please see the [CLDR pages](link to new one).

Closes #15879
2017-04-02 16:13:59 +03:00
Martin Staffa b4d1e5e492 docs(changelog): add release notes for 1.6.4 2017-03-31 10:48:25 +02:00
Jason Bedard 60394a9d91 fix($parse): standardize one-time literal vs non-literal and interceptors
Previously literal one-time bindings did not use the expression `inputs`, causing infinite digest issues with literal values. This often forces the use of deepEquals when watching one-time literals.

`ng-class` is one example of deepEquals which is no longer required.

This one-time/literal behavior is now also consistently propogated through interceptors.

Closes #15858
2017-03-31 09:36:49 +02:00
Jason Bedard f5ddb10b56 fix($parse): fix infinite digest errors when watching objects with .valueOf in literals
Closes #15867
2017-03-31 09:36:46 +02:00
Martin Staffa 6a448d3459 docs($animate): remove obsolete error doc 2017-03-30 23:20:35 +02:00
Martin Staffa 6a8c0f5f4a docs(faq): clarify the versioning strategy
- When do breaking changes appear
- Relationship with Semver
- Compatibility of modules

Closes #15845
2017-03-30 23:19:05 +02:00
Jason Bedard 584308fc06 refactor($parse): move duplicate $parse interpreter/compiler logic into Parser
- the construction of the AST is now in the Parser
- the assigning of the literal and constant flags is now in the Parser
- remove unused references to the lexer, $filter and options on the Parser
2017-03-30 23:09:51 +02:00
Jason Bedard 48ad2c44bf refactor($compile): reuse shared simpleCompare method 2017-03-30 23:09:39 +02:00
Jason Bedard 100998330a refactor($parse): make use of local variable instead of refetching property 2017-03-30 23:09:32 +02:00
Michał Gołębiowski b9e85c62be docs($compile): fix a typo in a deprecation warning 2017-03-29 14:15:02 +02:00
Michał Gołębiowski 77fad099d2 docs($compile): update a mention of preassigning bindings in controllers
The deprecation warning claimed the bindings are preassigned in controllers
by default which is not the case in 1.6.

Ref #15870
2017-03-29 14:01:43 +02:00
Jason Bedard e1f8a6e82b fix(ngModel): prevent internal scope reference from being copied
Fixes #15833
2017-03-27 22:45:46 -07:00
Jason Bedard 132d767647 refactor(ngModel): use local scope param in watcher 2017-03-27 22:45:46 -07:00
Michał Gołębiowski 9cde98cbc7 fix(jqLite): make jqLite invoke jqLite.cleanData as a method
The previous implementation of jqLite didn't use cleanData from the jqLite
object but instead used a cached version which maede it impossible to
monkey-patch jqLite.cleanData similarly to how you can do it in jQuery.

The cleanData method is not meant to be called directly by userland code;
its purpose is mainly to be able to be monkey-patched; therefore, the previous
implementation didn't make a lot of sense.

This commit enables one of the tests so far run only with jQuery to run with
jqLite as well.

(cherry-picked from bf5c2eef34)

Ref #8486
Ref #8695
Closes #15846
2017-03-27 14:05:43 +02:00
Michał Gołębiowski 1ddbb3ec3e chore(*): make ESLint happy 2017-03-27 13:58:38 +02:00
Michał Gołębiowski 34f40266b2 test(jqLite): test not firing $destroy on jqLite.cleanData with jQuery UI
So far it wasn't tested that Angular's logic for skipping it triggering
the $destroy event on jQuery.cleanData in the replaceWith internal function
works correctly when Angular is not the last one to patch the cleanData method
(e.g. if jQuery UI does the patching later). This commits adds the relevant
test.

(cherry-picked from bf7685abbd)

Ref #8486
2017-03-27 13:17:45 +02:00
Michał Gołębiowski b8c06e3f1b test(jQuery): run tests with jQuery 2.1, 2.2 & 3.2
Also, update jQuery 2.2.x mentions in the tutorial to 3.2.x.

Closes #15843
2017-03-27 11:25:29 +02:00
Peter Mertz a6cf648b3c docs($interval): Update interval promise docs
It's currently not clear when or why the promise returned by `$interval` resolves. This updates the docs to be more specific.

Closes #15862
2017-03-25 18:12:51 +02:00
Rodrigo aebde27f1b docs(filterFilter): clarify the comparator parameter
Closes #15827
2017-03-23 16:51:50 +02:00
Michał Gołębiowski c9be327d53 chore(yarn): rely on Travis built-in Yarn support, update Yarn in Jenkins
On Travis we now rely on built-in Yarn support and we only cache the Yarn cache,
not node_modules. This creates a more stable environment as we don't install
over previous node_modules state but we still won't download packages from the
internet in the second run for the same yarn.lock as Yarn takes packages from
its local cache if they exist there.

We install a new Yarn verison manually on Jenkins; the location of the install
script changed.

Closes #15851
2017-03-22 22:51:02 +01:00
Michał Gołębiowski 3dc7d22d90 chore($parse): make sure no one changes .toEqual(-0) to .toBe(-0) 2017-03-22 12:09:00 +01:00
Michał Gołębiowski c10c6cac74 docs(faq): document the AngularJS/jqLite deprecation strategy
Fixes #15282
2017-03-22 11:59:10 +01:00
Joshua J Wilborn cee2c4c569 docs ($compile): add error documentation for noslot error in $compile
Fixes #15790
Closes  #15828
2017-03-22 11:59:05 +01:00
Martin Staffa a5160c82dc docs($compile): add deprecation notice for preAssignBindingsEnabled 2017-03-22 11:56:33 +01:00
Martin Staffa 8853312197 docs(component-router): give deprecation notice red color 2017-03-22 11:56:33 +01:00
Raphael Jamet 049b24de21 docs($sce): overhaul the $sce service documentation
A big docs update around `$sce`:
There is a lot of content in there that is often misunderstood, and some of the
documentation starts to get really old too. Also fixed capitalization,
formatting, indentation and uniformized `@param` descriptions.

Closes #15735
2017-03-21 17:56:38 +02:00
Richard Kaufhold a9f987a0c9 feat($resource): add hasBody action configuration option
By default, only `PUT`, `POST` and `PATCH` requests have a body, but you can use
`hasBody` to configure any action to either have or not have a body, regardless
of its HTTP method.

Fixes #10128

Closes #12181
2017-03-21 12:45:46 +02:00
Georgios Kalpakas ad4a20d3d2 docs($sanitize): fix incorrect test description 2017-03-20 16:26:56 +02:00
BobChao87 b3972d1b65 docs($resource): encode ) in link
JSDoc to HTML converter was treating the close parenthesis in
`[MDN](...#toJson()_behavior)` as the final close parenthesis, thus resulting in
a broken link.
This commit fixes it by percent-encoding the parentesis in the link address.

Closes #15825
2017-03-20 12:53:38 +02:00
xfg c3e0d58b3c docs(ngMock/$httpBackend): add catch() block to example
Make the `it should fail authentication` test pass.

Closes #15822
2017-03-20 12:46:50 +02:00
TheRealMaxion f08156ea9b docs(tutorial/step_09) fix typo
Closes #15829
2017-03-20 12:33:53 +02:00
TheRealMaxion 4bacf5a5da docs(tutorial/step_04): fix typo (each --> its)
Closes #15826
2017-03-20 12:29:20 +02:00
Chirag Bhatia df88873bb7 fix($http): throw more informative error on invalid JSON response
Fixes #15695

Closes #15724
2017-03-14 14:49:24 +02:00
eeeqxxtg c4b1c5e8f1 docs(changelog): fix typo (resourceUrlWhiteList --> resourceUrlWhitelist)
Closes #15809
2017-03-14 11:30:53 +02:00
Ash Searle 9822711ad2 fix(dateFilter): correctly handle newlines in format string
Fixes #15794

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

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

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

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

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

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

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

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

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

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

Fixes #15644

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

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

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

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

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

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

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

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

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

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

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

Fixes #15624

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

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

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

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

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

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

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

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

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

Fixes #15629

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

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

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

Fixes #15474

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

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

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

Partly addresses #14074 and #14124.

Fixes #15510

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

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

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

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

Fixes #15603

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

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

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

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

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

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

Related to angular/protractor#789.

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

Fixes: #15586

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

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

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

Fixes #15144

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

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

This commit fixes both usecases by:

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

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

Fixes #11075
Fixes #12571
Fixes #15556

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

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

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

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

Fixes #15567

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

Fixes #15504

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

Fixes  #15523

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

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

Fixes #15477

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

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

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

Fixes #15492

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

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

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

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

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

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

  The cases that should benefit most are:

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

  The differences in operations per digest include:

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

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

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

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

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

  (*): on flatter structure

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

Closes #14404

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

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

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

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

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

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

Fixes #15331
Closes #15367

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

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

Fixes #15422

Closes #15424
2016-12-09 10:44:24 +02:00
225 changed files with 18784 additions and 22739 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
# https://editorconfig.org
# http://editorconfig.org
root = true
+1
View File
@@ -1,4 +1,5 @@
build/**
docs/bower_components/**
docs/app/assets/js/angular-bootstrap/**
docs/config/templates/**
node_modules/**
+3 -3
View File
@@ -1,4 +1,4 @@
<!-- General PR submission guidelines https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#submit-pr -->
<!-- General PR submission guidelines https://github.com/angular/angular.js/CONTRIBUTING.md#submit-pr -->
**What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)**
@@ -16,8 +16,8 @@
**Please check if the PR fulfills these requirements**
- [ ] The commit message follows our [guidelines](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits)
- [ ] Fix/Feature: [Docs](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#documentation) have been added/updated
- [ ] The commit message follows our [guidelines](../DEVELOPERS.md#commits)
- [ ] Fix/Feature: [Docs](../DEVELOPERS.md#documentation) have been added/updated
- [ ] Fix/Feature: Tests have been added; existing tests pass
**Other information**:
+1
View File
@@ -32,6 +32,7 @@ before_install:
before_script:
- du -sh ./node_modules || true
- "./scripts/travis/before_build.sh"
script:
- "./scripts/travis/build.sh"
+2 -1914
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -49,6 +49,7 @@ If you find a bug in the source code, you can help us by submitting an issue to
**Special Note for Localization Issues:** AngularJS uses the [Google Closure I18N library] to
generate its own I18N files (the ngLocale module). This means that any changes to these files
would be lost the next time that we import the library.
Since the Closure library i18n data is itself auto-generated from the data of the
[Common Locale Data Repository (CLDR)] project, errors in the data should
be reported there. See also the [Closure guide to i18n changes].
@@ -164,7 +165,6 @@ restarted.
* Re-run the AngularJS test suite to ensure tests are still passing.
* Commit your changes to your branch (e.g. `my-fix-branch`).
* Push the changes to your GitHub repository (this will update your Pull Request).
You can also amend the initial commits and force push them to the branch.
```shell
+13
View File
@@ -185,6 +185,7 @@ module.exports = function(grunt) {
'test/**/*.js',
'i18n/**/*.js',
'!docs/app/assets/js/angular-bootstrap/**',
'!docs/bower_components/**',
'!docs/config/templates/**',
'!src/angular.bind.js',
'!i18n/closure/**',
@@ -194,6 +195,16 @@ module.exports = function(grunt) {
},
build: {
scenario: {
dest: 'build/angular-scenario.js',
src: [
'bower_components/jquery/dist/jquery.js',
util.wrap([files['angularSrc'], files['angularScenario']], 'ngScenario/angular')
],
styles: {
css: ['css/angular.css', 'css/angular-scenario.css']
}
},
angular: {
dest: 'build/angular.js',
src: util.wrap([files['angularSrc']], 'angular'),
@@ -279,7 +290,9 @@ module.exports = function(grunt) {
files: [
'src/**/*.js',
'test/**/*.js',
'!test/ngScenario/DescribeSpec.js',
'!src/ng/directive/attrs.js', // legitimate xit here
'!src/ngScenario/**/*.js',
'!test/helpers/privateMocks*.js'
],
options: {
+29 -6
View File
@@ -28,7 +28,6 @@ var angularFiles = {
'src/ng/httpBackend.js',
'src/ng/interpolate.js',
'src/ng/interval.js',
'src/ng/intervalFactory.js',
'src/ng/jsonpCallbacks.js',
'src/ng/locale.js',
'src/ng/location.js',
@@ -41,7 +40,6 @@ var angularFiles = {
'src/ng/sanitizeUri.js',
'src/ng/sce.js',
'src/ng/sniffer.js',
'src/ng/taskTrackerFactory.js',
'src/ng/templateRequest.js',
'src/ng/testability.js',
'src/ng/timeout.js',
@@ -76,7 +74,6 @@ var angularFiles = {
'src/ng/directive/ngNonBindable.js',
'src/ng/directive/ngOptions.js',
'src/ng/directive/ngPluralize.js',
'src/ng/directive/ngRef.js',
'src/ng/directive/ngRepeat.js',
'src/ng/directive/ngShowHide.js',
'src/ng/directive/ngStyle.js',
@@ -106,13 +103,13 @@ var angularFiles = {
'src/ngAnimate/animateJs.js',
'src/ngAnimate/animateJsDriver.js',
'src/ngAnimate/animateQueue.js',
'src/ngAnimate/animateCache.js',
'src/ngAnimate/animation.js',
'src/ngAnimate/ngAnimateSwap.js',
'src/ngAnimate/module.js'
],
'ngCookies': [
'src/ngCookies/cookies.js',
'src/ngCookies/cookieStore.js',
'src/ngCookies/cookieWriter.js'
],
'ngMessageFormat': [
@@ -134,7 +131,6 @@ var angularFiles = {
],
'ngRoute': [
'src/shallowCopy.js',
'src/routeToRegExp.js',
'src/ngRoute/route.js',
'src/ngRoute/routeParams.js',
'src/ngRoute/directive/ngView.js'
@@ -144,13 +140,13 @@ var angularFiles = {
'src/ngSanitize/filter/linky.js'
],
'ngMock': [
'src/routeToRegExp.js',
'src/ngMock/angular-mocks.js',
'src/ngMock/browserTrigger.js'
],
'ngTouch': [
'src/ngTouch/touch.js',
'src/ngTouch/swipe.js',
'src/ngTouch/directive/ngClick.js',
'src/ngTouch/directive/ngSwipe.js'
],
'ngAria': [
@@ -158,8 +154,26 @@ var angularFiles = {
]
},
'angularScenario': [
'src/ngScenario/Scenario.js',
'src/ngScenario/Application.js',
'src/ngScenario/Describe.js',
'src/ngScenario/Future.js',
'src/ngScenario/ObjectModel.js',
'src/ngScenario/Runner.js',
'src/ngScenario/SpecRunner.js',
'src/ngScenario/dsl.js',
'src/ngScenario/matchers.js',
'src/ngScenario/output/Html.js',
'src/ngScenario/output/Json.js',
'src/ngScenario/output/Xml.js',
'src/ngScenario/output/Object.js'
],
'angularTest': [
'test/helpers/*.js',
'test/ngScenario/*.js',
'test/ngScenario/output/*.js',
'test/*.js',
'test/auto/*.js',
'test/ng/**/*.js',
@@ -180,15 +194,22 @@ var angularFiles = {
'test/jquery_remove.js',
'@angularSrc',
'@angularSrcModules',
'@angularScenario',
'@angularTest'
],
'karmaExclude': [
'test/jquery_alias.js',
'src/angular-bootstrap.js',
'src/ngScenario/angular-bootstrap.js',
'src/angular.bind.js'
],
'karmaScenario': [
'build/angular-scenario.js',
'build/docs/docs-scenario.js'
],
'karmaModules': [
'build/angular.js',
'@angularSrcModules',
@@ -211,11 +232,13 @@ var angularFiles = {
'test/jquery_alias.js',
'@angularSrc',
'@angularSrcModules',
'@angularScenario',
'@angularTest'
],
'karmaJqueryExclude': [
'src/angular-bootstrap.js',
'src/ngScenario/angular-bootstrap.js',
'test/jquery_remove.js',
'src/angular.bind.js'
]
+11 -7
View File
@@ -29,16 +29,19 @@ app.directive('bmPeWatch', function() {
};
});
//Executes the specified expression as a collection watcher
app.directive('bmPeWatchCollection', function() {
//Executes the specified expression as a watcher
//Adds a simple wrapper method to allow use of $watch instead of $watchCollection
app.directive('bmPeWatchLiteral', function($parse) {
function retZero() {
return 0;
}
return {
restrict: 'A',
compile: function($element, $attrs) {
$element.text($attrs.bmPeWatchCollection);
$element.text($attrs.bmPeWatchLiteral);
return function($scope, $element, $attrs) {
$scope.$watchCollection($attrs.bmPeWatchCollection, function(val) {
$element.text(val);
});
$scope.$watch($parse($attrs.bmPeWatchLiteral, retZero));
};
}
};
@@ -69,7 +72,8 @@ app.controller('DataController', function($scope, $rootScope) {
date2: new Date(Math.random() * Date.now()),
func: function() { return star; },
obj: data[i - 1],
keys: data[i - 1] && (data[i - 1].keys || Object.keys(data[i - 1]))
keys: data[i - 1] && (data[i - 1].keys || Object.keys(data[i - 1])),
constructor: data[i - 1]
});
}
+35 -45
View File
@@ -16,6 +16,12 @@
<label for="complexPath">Complex Paths</label>
</li>
<li>
<input type="radio" ng-model="expressionType" value="constructorPath" id="constructorPath">
<label for="constructorPath">Constructor Paths</label>
($parse special cases "constructor" for security)
</li>
<li>
<input type="radio" ng-model="expressionType" value="fieldAccess" id="fieldAccess">
<label for="fieldAccess">Field Accessors</label>
@@ -60,16 +66,6 @@
<input type="radio" ng-model="expressionType" value="arrayLiterals" id="arrayLiterals">
<label for="arrayLiterals">Array Literals</label>
</li>
<li>
<input type="radio" ng-model="expressionType" value="watchCollection" id="watchCollection">
<label for="watchCollection">$watchCollection</label>
</li>
<li>
<input type="radio" ng-model="expressionType" value="watchCollectionLiterals" id="watchCollectionLiterals">
<label for="watchCollectionLiterals">$watchCollection Literals</label>
</li>
</ul>
<!--
@@ -92,6 +88,17 @@
<span bm-pe-watch="row.keys"></span>
</li>
<li ng-switch-when="constructorPath" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="row.index"></span>
<span bm-pe-watch="row.constructor.index"></span>
<span bm-pe-watch="row.constructor.index"></span>
<span bm-pe-watch="row.constructor.index"></span>
<span bm-pe-watch="row.constructor.constructor.index"></span>
<span bm-pe-watch="row.constructor.constructor.index"></span>
<span bm-pe-watch="row.constructor.constructor.constructor.index"></span>
<span bm-pe-watch="row.constructor.constructor.constructor.index"></span>
</li>
<li ng-switch-when="complexPath" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="row.index"></span>
<span bm-pe-watch="row.num0"></span>
@@ -208,44 +215,27 @@
</li>
<li ng-switch-when="objectLiterals" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="{foo: rowIdx}"></span>
<span bm-pe-watch="{foo: row, bar: rowIdx}"></span>
<span bm-pe-watch="{0: row, 1: rowIdx, 2: 3}"></span>
<span bm-pe-watch="{str: 'foo', num: rowIdx, b: true}"></span>
<span bm-pe-watch="{a: {b: {c: {d: {e: {f: rowIdx}}}}}}"></span>
<span bm-pe-watch="{a: rowIdx, b: 1, c: 2, d: 3, e: 4, f: 5, g: rowIdx, h: 6, i: 7, j: 8, k: rowIdx}"></span>
<span bm-pe-watch-literal="{foo: rowIdx}"></span>
<span bm-pe-watch-literal="{foo: row, bar: rowIdx}"></span>
<span bm-pe-watch-literal="{0: row, 1: rowIdx, 2: 3}"></span>
<span bm-pe-watch-literal="{str: 'foo', num: rowIdx, b: true}"></span>
<span bm-pe-watch-literal="{a: {b: {c: {d: {e: {f: rowIdx}}}}}}"></span>
<span bm-pe-watch-literal="{a: rowIdx, b: 1, c: 2, d: 3, e: 4, f: 5, g: rowIdx, h: 6, i: 7, j: 8, k: rowIdx}"></span>
</li>
<li ng-switch-when="arrayLiterals" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="[rowIdx]"></span>
<span bm-pe-watch="[rowIdx, 0]"></span>
<span bm-pe-watch="[rowIdx, 0, 1]"></span>
<span bm-pe-watch="[rowIdx, 0, 1, 2]"></span>
<span bm-pe-watch="[rowIdx, 0, 1, 2, 3]"></span>
<span bm-pe-watch="[[], [rowIdx], [], [], [3], [[[]]]]"></span>
<span bm-pe-watch="[rowIdx, undefined, null, true, false]"></span>
<span bm-pe-watch="[[][0], [0][0], [][rowIdx]]"></span>
<span bm-pe-watch="[0, rowIdx]"></span>
<span bm-pe-watch="[0, 1, rowIdx]"></span>
<span bm-pe-watch="[0, 1, 2, rowIdx]"></span>
<span bm-pe-watch="[0, 1, 2, 3, rowIdx]"></span>
</li>
<li ng-switch-when="watchCollection" ng-repeat="(rowIdx, row) in data">
<span bm-pe-watch-collection="data"></span>
<span bm-pe-watch-collection="row.keys"></span>
<span bm-pe-watch-collection="thisProbablyDoesntHaveAValue"></span>
</li>
<li ng-switch-when="watchCollectionLiterals" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch-collection="[rowIdx, row]"></span>
<span bm-pe-watch-collection="[rowIdx, row, num0, str0, date0, obj, g, h, i, j, k, l, m, n, o, p]"></span>
<span bm-pe-watch-collection="{a: rowIdx, b: row, c: num0, d: str0, e: date0, f: obj, g: g, h: h, i: i, j: j, k: k, l: l, m: m, n: n, o: o, p: p}"></span>
<!-- primitive/valueOf-compatible -->
<span bm-pe-watch-collection="[rowIdx, row]"></span>
<span bm-pe-watch-collection="[rowIdx, num0, str0, date0, date1, h, i, j, k, l, m, n, o, p]"></span>
<span bm-pe-watch-collection="{a: rowIdx, c: num0, d: str0, e: date0, g: date1, h: h, i: i, j: j, k: k, l: l, m: m, n: n, o: o, p: p}"></span>
<span bm-pe-watch-literal="[rowIdx]"></span>
<span bm-pe-watch-literal="[rowIdx, 0]"></span>
<span bm-pe-watch-literal="[rowIdx, 0, 1]"></span>
<span bm-pe-watch-literal="[rowIdx, 0, 1, 2]"></span>
<span bm-pe-watch-literal="[rowIdx, 0, 1, 2, 3]"></span>
<span bm-pe-watch-literal="[[], [rowIdx], [], [], [3], [[[]]]]"></span>
<span bm-pe-watch-literal="[rowIdx, undefined, null, true, false]"></span>
<span bm-pe-watch-literal="[[][0], [0][0], [][rowIdx]]"></span>
<span bm-pe-watch-literal="[0, rowIdx]"></span>
<span bm-pe-watch-literal="[0, 1, rowIdx]"></span>
<span bm-pe-watch-literal="[0, 1, 2, rowIdx]"></span>
<span bm-pe-watch-literal="[0, 1, 2, 3, rowIdx]"></span>
</li>
</ul>
</div>
@@ -1,9 +0,0 @@
'use strict';
angular.module('repeatAnimateBenchmark', ['ngAnimate'])
.config(function($animateProvider) {
$animateProvider.classNameFilter(/animate-/);
})
.run(function($rootScope) {
$rootScope.fileType = 'classfilter';
});
@@ -1,6 +0,0 @@
'use strict';
angular.module('repeatAnimateBenchmark', [])
.run(function($rootScope) {
$rootScope.fileType = 'noanimate';
});
-7
View File
@@ -1,7 +0,0 @@
'use strict';
angular.module('repeatAnimateBenchmark', ['ngAnimate'])
.run(function($rootScope) {
$rootScope.fileType = 'default';
});
-24
View File
@@ -1,24 +0,0 @@
/* eslint-env node */
'use strict';
module.exports = function(config) {
config.set({
scripts: [
{
id: 'angular',
src: '/build/angular.js'
},
{
id: 'angular-animate',
src: '/build/angular-animate.js'
},
{
id: 'app',
src: 'app.js'
},
{
src: 'common.js'
}]
});
};
-120
View File
@@ -1,120 +0,0 @@
'use strict';
(function() {
var app = angular.module('repeatAnimateBenchmark');
app.config(function($compileProvider, $animateProvider) {
if ($compileProvider.debugInfoEnabled) {
$compileProvider.debugInfoEnabled(false);
}
});
app.run(function($animate) {
if ($animate.enabled) {
$animate.enabled(true);
}
});
app.controller('DataController', function($scope, $rootScope, $animate) {
var totalRows = 500;
var totalColumns = 20;
var data = $scope.data = [];
function fillData() {
if ($animate.enabled) {
$animate.enabled($scope.benchmarkType !== 'globallyDisabled');
}
for (var i = 0; i < totalRows; i++) {
data[i] = [];
for (var j = 0; j < totalColumns; j++) {
data[i][j] = {
i: i
};
}
}
}
benchmarkSteps.push({
name: 'enter',
fn: function() {
$scope.$apply(function() {
fillData();
});
}
});
benchmarkSteps.push({
name: 'leave',
fn: function() {
$scope.$apply(function() {
data = $scope.data = [];
});
}
});
});
app.directive('disableAnimations', function($animate) {
return {
link: {
pre: function(s, e) {
$animate.enabled(e, false);
}
}
};
});
app.directive('noop', function($animate) {
return {
link: {
pre: angular.noop
}
};
});
app.directive('baseline', function($document) {
return {
restrict: 'E',
link: function($scope, $element) {
var document = $document[0];
var i, j, row, cell, comment;
var template = document.createElement('span');
template.setAttribute('ng-repeat', 'foo in foos');
template.classList.add('ng-scope');
template.appendChild(document.createElement('span'));
template.appendChild(document.createTextNode(':'));
function createList() {
for (i = 0; i < $scope.data.length; i++) {
row = document.createElement('div');
$element[0].appendChild(row);
for (j = 0; j < $scope.data[i].length; j++) {
cell = template.cloneNode(true);
row.appendChild(cell);
cell.childNodes[0].textContent = i;
cell.ng339 = 'xxx';
comment = document.createComment('ngRepeat end: bar in foo');
row.appendChild(comment);
}
comment = document.createComment('ngRepeat end: foo in foos');
$element[0].appendChild(comment);
}
}
$scope.$watch('data.length', function(newVal) {
if (newVal === 0) {
while ($element[0].firstChild) {
$element[0].removeChild($element[0].firstChild);
}
} else {
createList();
}
});
}
};
});
})();
-70
View File
@@ -1,70 +0,0 @@
<div ng-app="repeatAnimateBenchmark" ng-cloak>
<div ng-controller="DataController">
<div class="container-fluid">
<p>
Tests rendering of an ngRepeat with 500 elements.<br>
Animations can be enabled / disabled in different ways.<br>
Two tests require reloading the app with different module / app configurations.
</p>
<div><label><input type="radio" ng-model="benchmarkType" value="none">none: </label></div>
<div><label><input type="radio" ng-model="benchmarkType" value="baseline">baseline (vanilla Javascript): </label></div>
<div><label><input type="radio" ng-model="benchmarkType" ng-disabled="fileType !== 'default'" value="enabled">enabled : </label> (requires <a href="./">app.js</a>)</div>
<div><label><input type="radio" ng-model="benchmarkType" ng-disabled="fileType !== 'default' && fileType !== 'classfilter'" value="globallyDisabled">globally disabled:</label> (requires <a href="./">app.js</a> or <a href="?app=app-classfilter.js">app-classfilter.js</a>)</div>
<div><label><input type="radio" ng-model="benchmarkType" ng-disabled="fileType !== 'default'" value="disabledParentElement">disabled by $animate.enabled() on parent element: </label> (requires <a href="./">app.js</a>)</div>
<div><label><input type="radio" ng-model="benchmarkType" ng-disabled="fileType !== 'noanimate'" value="noanimate">Without ngAnimate:</label> (requires <a href="?app=app-noanimate.js">app-noanimate.js</a>)</div>
<div><label><input type="radio" ng-model="benchmarkType" ng-disabled="fileType !== 'classfilter'" value="disabledClassFilter">disabled by classNameFilter on element:</label> (requires <a href="?app=app-classfilter.js">app-classfilter.js</a>)</div>
<ng-switch on="benchmarkType">
<baseline ng-switch-when="baseline">
</baseline>
<div ng-switch-when="noanimate">
<div noop>
<div ng-repeat="row in data">
<span ng-repeat="column in row">
<span>{{column.i}}</span>
</span>
</div>
</div>
</div>
<div ng-switch-when="enabled">
<div noop>
<div ng-repeat="row in data">
<span ng-repeat="column in row">
<span>{{column.i}}</span>
</span>
</div>
</div>
</div>
<div ng-switch-when="globallyDisabled">
<div noop>
<div ng-repeat="row in data">
<span ng-repeat="column in row">
<span>{{column.i}}</span>
</span>
</div>
</div>
</div>
<div ng-switch-when="disabledClassFilter">
<div noop>
<div ng-repeat="row in data">
<span class="disable-animations" ng-repeat="column in row">
<span>{{column.i}}</span>
</span>
</div>
</div>
</div>
<div ng-switch-when="disabledParentElement">
<div disable-animations>
<div ng-repeat="row in data">
<span ng-repeat="column in row">
<span>{{column.i}}</span>
</span>
</div>
</div>
</div>
</ng-switch>
</div>
</div>
</div>
+1 -2
View File
@@ -13,8 +13,7 @@ body {
text-align: center;
}
#json,
#xml {
#json, #xml {
display: none;
}
+2 -6
View File
@@ -1,11 +1,7 @@
@charset "UTF-8";
[ng\:cloak],
[ng-cloak],
[data-ng-cloak],
[x-ng-cloak],
.ng-cloak,
.x-ng-cloak,
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
.ng-cloak, .x-ng-cloak,
.ng-hide:not(.ng-hide-animate) {
display: none !important;
}
+2 -2
View File
@@ -8,7 +8,7 @@
describe('table of contents', function() {
it('on provider pages', function() {
browser.get('build/docs/index.html#!/api/ng/provider/$controllerProvider');
browser.get('build/docs/index.html#!/api/ng/provider/$interpolateProvider');
var toc = element.all(by.css('toc-container > div > toc-tree'));
toc.getText().then(function(text) {
@@ -19,7 +19,7 @@ describe('table of contents', function() {
var tocFirstLevel = element.all(by.css('toc-container > div > toc-tree > ul > li'));
tocFirstLevel.then(function(match) {
expect(match.length).toBe(2);
expect(match.length).toBe(3);
expect(match[1].all(by.css('li')).count()).toBe(2);
});
+1 -1
View File
@@ -98,7 +98,7 @@ directivesModule
bindings: {
items: '<'
},
controller: ['$location', /** @this */ function($location) {
controller: ['$location', function TocTreeController($location) {
this.path = $location.path().replace(/^\/?(.+?)(\/index)?\/?$/, '$1');
}]
})
-1
View File
@@ -148,7 +148,6 @@ module.exports = new Package('angularjs', [
.config(function(checkAnchorLinksProcessor) {
checkAnchorLinksProcessor.base = '/';
checkAnchorLinksProcessor.errorOnUnmatchedLinks = true;
// We are only interested in docs that have an area (i.e. they are pages)
checkAnchorLinksProcessor.checkDoc = function(doc) { return doc.area; };
})
+1 -23
View File
@@ -5,36 +5,18 @@
* @description
* Process "error" docType docs and generate errorNamespace docs
*/
module.exports = function errorDocsProcessor(log, errorNamespaceMap, getMinerrInfo) {
module.exports = function errorDocsProcessor(errorNamespaceMap, getMinerrInfo) {
return {
$runAfter: ['tags-extracted'],
$runBefore: ['extra-docs-added'],
$process: function(docs) {
// Get the extracted min errors to compare with the error docs, and report any mismatch
var collectedErrors = require('../../../build/errors.json').errors;
var flatErrors = [];
for (var namespace in collectedErrors) {
for (var error in collectedErrors[namespace]) {
flatErrors.push(namespace + ':' + error);
}
}
// Create error namespace docs and attach error docs to each
docs.forEach(function(doc) {
var parts, namespaceDoc;
if (doc.docType === 'error') {
var matchingMinErr = flatErrors.indexOf(doc.name);
if (matchingMinErr === -1) {
log.warn('Error doc: ' + doc.name + ' has no matching min error');
} else {
flatErrors.splice(matchingMinErr, 1);
}
// Parse out the error info from the id
parts = doc.name.split(':');
doc.namespace = parts[0];
@@ -59,10 +41,6 @@ module.exports = function errorDocsProcessor(log, errorNamespaceMap, getMinerrIn
}
});
flatErrors.forEach(function(value) {
log.warn('No error doc exists for min error: ' + value);
});
errorNamespaceMap.forEach(function(errorNamespace) {
docs.push(errorNamespace);
});
+5 -1
View File
@@ -222,7 +222,11 @@ Use the ngCookies module to handle cookie management within your application.
{@link ngCookies#service Services / Factories}
</td>
<td>
The {@link ngCookies.$cookies $cookies} service is a convenient wrapper to store simple data within browser cookies.
The following services are used for cookie management:
<ul>
<li>The {@link ngCookies.$cookies $cookie} service is a convenient wrapper to store simple data within browser cookies.</li>
<li>{@link ngCookies.$cookieStore $cookieStore} is used to store more complex data using serialization.</li>
</ul>
</td>
</tr>
</table>
@@ -1,13 +0,0 @@
@ngdoc error
@name $compile:ctxoverride
@fullName DOM Property Security Context Override
@description
This error occurs when the security context for a property is defined via {@link ng.$compileProvider#addPropertySecurityContext addPropertySecurityContext()} multiple times under different security contexts.
For example:
```js
$compileProvider.addPropertySecurityContext("my-element", "src", $sce.MEDIA_URL);
$compileProvider.addPropertySecurityContext("my-element", "src", $sce.RESOURCE_URL); //throws
```
+7 -9
View File
@@ -10,15 +10,13 @@ myModule.directive('directiveName', function factory() {
return {
...
scope: {
'localName': '@', // OK
'localName2': '&attr', // OK
'localName3': '<?attr', // OK
'localName4': ' = attr', // OK
'localName5': ' =*attr', // OK
'localName6': 'attr', // ERROR: missing mode @&=<
'localName7': 'attr=', // ERROR: must be prefixed with @&=<
'localName8': '=attr?', // ERROR: ? must come directly after the mode
'localName9': '<*' // ERROR: * is only valid with =
'attrName': '@', // OK
'attrName2': '=localName', // OK
'attrName3': '<?localName', // OK
'attrName4': ' = name', // OK
'attrName5': 'name', // ERROR: missing mode @&=
'attrName6': 'name=', // ERROR: must be prefixed with @&=
'attrName7': '=name?', // ERROR: ? must come directly after the mode
}
...
}
+3 -31
View File
@@ -3,34 +3,6 @@
@fullName Missing required attribute
@description
This error may occur only when {@link $compileProvider#strictComponentBindingsEnabled `$compileProvider.strictComponentBindingsEnabled`} is set to `true`.
If that is the case, then all {@link $compileProvider#component component} controller bindings and
{@link $compileProvider#directive directive} scope / controller bindings that are non-optional,
must be provided when the directive is instantiated.
To make a binding optional, add '?' to the definition.
## Example:
```js
app.component('myTest', {
bindings: {
first: '=?', // optional
second: '='
},
controller: function() {
...
},
template: '...'
});
```
This component will throw `missingattr` for the `second` binding when used as follows:
```html
<my-test></my-test>
```
This error may occur only when `$compileProvider.strictComponentBindingsEnabled` is set to `true`.
Then all attributes mentioned in `bindings` without `?` must be set. If one or more aren't set,
the first one will throw an error.
@@ -1,12 +1,12 @@
@ngdoc error
@name $compile:nodomevents
@fullName Event Attribute/Property Binding
@fullName Interpolated Event Attributes
@description
This error occurs when one tries to create a binding for event handler attributes or properties like `onclick`, `onload`, `onsubmit`, etc.
This error occurs when one tries to create a binding for event handler attributes like `onclick`, `onload`, `onsubmit`, etc.
There is no practical value in binding to these attributes/properties and doing so only exposes your application to security vulnerabilities like XSS.
For these reasons binding to event handler attributes and properties (`formaction` and all starting with `on`) is not supported.
There is no practical value in binding to these attributes and doing so only exposes your application to security vulnerabilities like XSS.
For these reasons binding to event handler attributes (all attributes that start with `on` and `formaction` attribute) is not supported.
An example code that would allow XSS vulnerability by evaluating user input in the window context could look like this:
@@ -17,4 +17,4 @@ An example code that would allow XSS vulnerability by evaluating user input in t
Since the `onclick` evaluates the value as JavaScript code in the window context, setting the `username` model to a value like `javascript:alert('PWND')` would result in script injection when the `div` is clicked.
Please use the `ng-*` or `ng-on-*` versions instead (such as `ng-click` or `ng-on-click` rather than `onclick`).
-12
View File
@@ -1,12 +0,0 @@
@ngdoc error
@name $compile:srcset
@fullName Invalid value passed to `attr.$set('srcset', value)`
@description
This error occurs if you try to programmatically set the `srcset` attribute with a non-string value.
This can be the case if you tried to avoid the automatic sanitization of the `srcset` value by
passing a "trusted" value provided by calls to `$sce.trustAsMediaUrl(value)`.
If you want to programmatically set explicitly trusted unsafe URLs, you should use `$sce.trustAsHtml`
on the whole `img` tag and inject it into the DOM using the `ng-bind-html` directive.
+11
View File
@@ -0,0 +1,11 @@
@ngdoc error
@name $compile:tpload
@fullName Error Loading Template
@description
This error occurs when {@link ng.$compile `$compile`} attempts to fetch a template from some URL, and the request fails.
To resolve this error, ensure that the URL of the template is spelled correctly and resolves to correct absolute URL.
The [Chrome Developer Tools](https://developers.google.com/chrome-developer-tools/docs/network#network_panel_overview) might also be helpful in determining why the request failed.
If you are using {@link ng.$templateCache} to pre-load templates, ensure that the cache was populated with the template.
+1 -1
View File
@@ -9,7 +9,7 @@ value is `JSON_CALLBACK`.
`$http` JSONP requests need to attach a callback query parameter to the URL. The name of this
parameter is specified in the configuration object (or in the defaults) via the `jsonpCallbackParam`
property. You must not provide your own parameter with this name in the configuration of the request.
property. You must not provide your own parameter with this name in the configuratio of the request.
In previous versions of AngularJS, you specified where to add the callback parameter value via the
`JSON_CALLBACK` placeholder. This is no longer allowed.
@@ -1,25 +0,0 @@
@ngdoc error
@name $interval:badprom
@fullName Non-$interval promise
@description
This error occurs when calling {@link ng.$interval#cancel $interval.cancel()} with a promise that
was not generated by the {@link ng.$interval $interval} service. This can, for example, happen when
calling {@link ng.$q#the-promise-api then()/catch()} on the returned promise, which creates a new
promise, and pass that new promise to {@link ng.$interval#cancel $interval.cancel()}.
Example of incorrect usage that leads to this error:
```js
var promise = $interval(doSomething, 1000, 5).then(doSomethingElse);
$interval.cancel(promise);
```
To fix the example above, keep a reference to the promise returned by
{@link ng.$interval $interval()} and pass that to {@link ng.$interval#cancel $interval.cancel()}:
```js
var promise = $interval(doSomething, 1000, 5);
var newPromise = promise.then(doSomethingElse);
$interval.cancel(promise);
```
@@ -1,18 +0,0 @@
@ngdoc error
@name $templateRequest:tpload
@fullName Error Loading Template
@description
This error occurs when {@link $templateRequest} attempts to fetch a template from some URL, and
the request fails.
The template URL might be defined in a directive/component definition, an instance of `ngInclude`,
an instance of `ngMessagesInclude` or a templated route in a `$route` route definition.
To resolve this error, ensure that the URL of the template is spelled correctly and resolves to
correct absolute URL.
The [Chrome Developer Tools](https://developers.google.com/chrome-developer-tools/docs/network#network_panel_overview)
might also be helpful in determining why the request failed.
If you are using {@link ng.$templateCache} to pre-load templates, ensure that the cache was
populated with the template.
-25
View File
@@ -1,25 +0,0 @@
@ngdoc error
@name $timeout:badprom
@fullName Non-$timeout promise
@description
This error occurs when calling {@link ng.$timeout#cancel $timeout.cancel()} with a promise that
was not generated by the {@link ng.$timeout $timeout} service. This can, for example, happen when
calling {@link ng.$q#the-promise-api then()/catch()} on the returned promise, which creates a new
promise, and pass that new promise to {@link ng.$timeout#cancel $timeout.cancel()}.
Example of incorrect usage that leads to this error:
```js
var promise = $timeout(doSomething, 1000).then(doSomethingElse);
$timeout.cancel(promise);
```
To fix the example above, keep a reference to the promise returned by
{@link ng.$timeout $timeout()} and pass that to {@link ng.$timeout#cancel $timeout.cancel()}:
```js
var promise = $timeout(doSomething, 1000);
var newPromise = promise.then(doSomethingElse);
$timeout.cancel(promise);
```
+6 -3
View File
@@ -3,9 +3,12 @@
@fullName Unsupported Selector Lookup
@description
In order to keep AngularJS small, AngularJS implements only a subset of the selectors in {@link angular.element#angularjs-s-jqlite jqLite}.
In order to keep AngularJS small, AngularJS implements only a subset of the selectors in
{@link angular.element#angularjs-s-jqlite jqLite}.
This error occurs when a jqLite instance is invoked with a selector other than this subset.
In order to resolve this error, rewrite your code to only use tag name selectors and manually traverse the DOM using the APIs provided by jqLite.
In order to resolve this error, rewrite your code to only use tag name selectors and manually
traverse the DOM using the APIs provided by jqLite.
Alternatively, you can include a full version of jQuery, which AngularJS will automatically use and that will make all selectors available.
Alternatively, you can include a full version of jQuery, which AngularJS will automatically use
and that will make all selectors available.
-17
View File
@@ -1,17 +0,0 @@
@ngdoc error
@name ngRef:noctrl
@fullName A controller for the value of `ngRefRead` could not be found on the element.
@description
This error occurs when the {@link ng.ngRef ngRef directive} specifies
a value in `ngRefRead` that cannot be resolved to a directive / component controller.
Causes for this error can be:
1. Your `ngRefRead` value has a typo.
2. You have a typo in the *registered* directive / component name.
3. The directive / component does not have a controller.
Note that `ngRefRead` takes the name of the component / directive, not the name of controller, and
also not the combination of directive and 'Controller'. For example, for a directive called 'myDirective',
the correct declaration is `<div ng-ref="$ctrl.ref" ng-ref-read="myDirective">`.
-27
View File
@@ -1,27 +0,0 @@
@ngdoc error
@name ngRef:nonassign
@fullName Non-Assignable Expression
@description
This error occurs when ngRef defines an expression that is not-assignable.
In order for ngRef to work, it must be possible to write the reference into the path defined with the expression.
For example, the following expressions are non-assignable:
```
<my-directive ng-ref="{}"></my-directive>
<my-directive ng-ref="myFn()"></my-directive>
<!-- missing attribute value is also invalid -->
<my-directive ng-ref></my-directive>
```
To resolve this error, use a path expression that is assignable:
```
<my-directive ng-ref="$ctrl.reference"></my-directive>
```
+116 -32
View File
@@ -3,7 +3,7 @@
@sortOrder 500
@description
# Using the `$location` service
# What does it do?
The `$location` service parses the URL in the browser address bar (based on [`window.location`](https://developer.mozilla.org/en/window.location)) and makes the URL available to
your application. Changes to the URL in the address bar are reflected into the `$location` service and
@@ -76,7 +76,7 @@ the current URL in the browser.
It does not cause a full page reload when the browser URL is changed. To reload the page after
changing the URL, use the lower-level API, `$window.location.href`.
## General overview of the API
# General overview of the API
The `$location` service can behave differently, depending on the configuration that was provided to
it when it was instantiated. The default configuration is suitable for many applications, for
@@ -85,7 +85,7 @@ others customizing the configuration can enable new features.
Once the `$location` service is instantiated, you can interact with it via jQuery-style getter and
setter methods that allow you to get or change the current URL in the browser.
### `$location` service configuration
## `$location` service configuration
To configure the `$location` service, retrieve the
{@link ng.$locationProvider $locationProvider} and set the parameters as follows:
@@ -113,12 +113,12 @@ To configure the `$location` service, retrieve the
Prefix used for Hashbang URLs (used in Hashbang mode or in legacy browsers in HTML5 mode).<br />
Default: `'!'`
#### Example configuration
### Example configuration
```js
$locationProvider.html5Mode(true).hashPrefix('*');
```
### Getter and setter methods
## Getter and setter methods
`$location` service provides getter methods for read-only parts of the URL (absUrl, protocol, host,
port) and getter / setter methods for url, path, search, hash:
@@ -137,7 +137,7 @@ change multiple segments in one go, chain setters like this:
$location.path('/newValue').search({key: value});
```
### Replace method
## Replace method
There is a special `replace` method which can be used to tell the $location service that the next
time the $location service is synced with the browser, the last history record should be replaced
@@ -173,7 +173,7 @@ encoded.
`/path?search=a&b=c#hash`. The segments are encoded as well.
## Hashbang and HTML5 Modes
# Hashbang and HTML5 Modes
`$location` service has two configuration modes which control the format of the URL in the browser
address bar: **Hashbang mode** (the default) and the **HTML5 mode** which is based on using the
@@ -221,7 +221,7 @@ facilitate the browser URL change and history management.
</tbody>
</table>
### Hashbang mode (default mode)
## Hashbang mode (default mode)
In this mode, `$location` uses Hashbang URLs in all browsers.
AngularJS also does not intercept and rewrite links in this mode. I.e. links work
@@ -229,7 +229,7 @@ as expected and also perform full page reloads when other parts of the url
than the hash fragment was changed.
#### Example
### Example
```js
it('should show example', function() {
@@ -255,7 +255,7 @@ it('should show example', function() {
});
```
### HTML5 mode
## HTML5 mode
In HTML5 mode, the `$location` service getters and setters interact with the browser URL address
through the HTML5 history API. This allows for use of regular URL path and search segments,
@@ -271,7 +271,7 @@ Note that in this mode, AngularJS intercepts all links (subject to the "Html lin
and updates the url in a way that never performs a full page reload.
#### Example
### Example
```js
it('should show example', function() {
@@ -320,14 +320,14 @@ it('should show example (when browser doesn\'t support HTML5 mode', function() {
});
```
#### Fallback for legacy browsers
### Fallback for legacy browsers
For browsers that support the HTML5 history API, `$location` uses the HTML5 history API to write
path and search. If the history API is not supported by a browser, `$location` supplies a Hashbang
URL. This frees you from having to worry about whether the browser viewing your app supports the
history API or not; the `$location` service makes this transparent to you.
#### HTML link rewriting
### HTML link rewriting
When you use HTML5 history API mode, you will not need special hashbang links. All you have to do
is specify regular URL links, such as: `<a href="/some?foo=bar">link</a>`
@@ -361,7 +361,7 @@ Note that [attribute name normalization](guide/directive#normalization) does not
`'internalLink'` will **not** match `'internal-link'`.
#### Relative links
### Relative links
Be sure to check all relative links, images, scripts etc. AngularJS requires you to specify the url
base in the head of your main html file (`<base href="/my-base/index.html">`) unless `html5Mode.requireBase`
@@ -374,14 +374,14 @@ will only change `$location.hash()` and not modify the url otherwise. This is us
to anchors on the same page without needing to know on which page the user currently is.
#### Server side
### Server side
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links
to entry point of your application (e.g. index.html). Requiring a `<base>` tag is also important for
this case, as it allows AngularJS to differentiate between the part of the url that is the application
base and the path that should be handled by the application.
#### Base href constraints
### Base href constraints
The `$location` service is not able to function properly if the current URL is outside the URL given
as the base href. This can have subtle confusing consequences...
@@ -403,7 +403,7 @@ legacy browsers and hashbang links in modern browser:
- Modern browser will rewrite hashbang URLs to regular URLs.
- Older browsers will redirect regular URLs to hashbang URLs.
#### Example
### Example
Here you can see two `$location` instances that show the difference between **Html5 mode** and **Html5 Fallback mode**.
Note that to simulate different levels of browser support, the `$location` instances are connected to
@@ -415,7 +415,7 @@ redirect to regular / hashbang url, as this conversion happens only during parsi
In these examples we use `<base href="/base/index.html" />`. The inputs represent the address bar of the browser.
##### Browser in HTML5 mode
#### Browser in HTML5 mode
<example module="html5-mode" name="location-html5-mode">
<file name="index.html">
<div ng-controller="LocationController">
@@ -565,7 +565,7 @@ In these examples we use `<base href="/base/index.html" />`. The inputs represen
</example>
##### Browser in HTML5 Fallback mode (Hashbang mode)
#### Browser in HTML5 Fallback mode (Hashbang mode)
<example module="hashbang-mode" name="location-hashbang-mode">
<file name="index.html">
<div ng-controller="LocationController">
@@ -718,15 +718,15 @@ In these examples we use `<base href="/base/index.html" />`. The inputs represen
</example>
## Caveats
# Caveats
### Page reload navigation
## Page reload navigation
The `$location` service allows you to change only the URL; it does not allow you to reload the
page. When you need to change the URL and reload the page or navigate to a different page, please
use a lower level API, {@link ng.$window $window.location.href}.
### Using $location outside of the scope life-cycle
## Using $location outside of the scope life-cycle
`$location` knows about AngularJS's {@link ng.$rootScope.Scope scope} life-cycle. When a URL changes in
the browser it updates the `$location` and calls `$apply` so that all
@@ -738,7 +738,7 @@ propagate this change into browser and will notify all the {@link ng.$rootScope.
When you want to change the `$location` from outside AngularJS (for example, through a DOM Event or
during testing) - you must call `$apply` to propagate the changes.
### $location.path() and ! or / prefixes
## $location.path() and ! or / prefixes
A path should always begin with forward slash (`/`); the `$location.path()` setter will add the
forward slash if it is missing.
@@ -746,17 +746,22 @@ forward slash if it is missing.
Note that the `!` prefix in the hashbang mode is not part of `$location.path()`; it is actually
`hashPrefix`.
### Crawling your app
## Crawling your app
Most modern search engines are able to crawl AJAX applications with dynamic content, provided all
included resources are available to the crawler bots.
To allow indexing of your AJAX application, you have to add special meta tag in the head section of
your document:
There also exists a special
[AJAX crawling scheme](http://code.google.com/web/ajaxcrawling/docs/specification.html) developed by
Google that allows bots to crawl the static equivalent of a dynamically generated page,
but this schema has been deprecated, and support for it may vary by search engine.
```html
<meta name="fragment" content="!" />
```
## Testing with the $location service
This will cause crawler bot to request links with `_escaped_fragment_` param so that your server
can recognize the crawler and serve a HTML snapshots. For more information about this technique,
see [Making AJAX Applications
Crawlable](http://code.google.com/web/ajaxcrawling/docs/specification.html).
# Testing with the $location service
When using `$location` service during testing, you are outside of the angular's {@link
ng.$rootScope.Scope scope} life-cycle. This means it's your responsibility to call `scope.$apply()`.
@@ -779,6 +784,85 @@ describe('serviceUnderTest', function() {
});
```
# Migrating from earlier AngularJS releases
In earlier releases of AngularJS, `$location` used `hashPath` or `hashSearch` to process path and
search methods. With this release, the `$location` service processes path and search methods and
then uses the information it obtains to compose hashbang URLs (such as
`http://server.com/#!/path?search=a`), when necessary.
## Changes to your code
<table class="table">
<thead>
<tr class="head">
<th>Navigation inside the app</th>
<th>Change to</th>
</tr>
</thead>
<tbody>
<tr>
<td>$location.href = value<br />$location.hash = value<br />$location.update(value)<br
/>$location.updateHash(value)</td>
<td>$location.path(path).search(search)</td>
</tr>
<tr>
<td>$location.hashPath = path</td>
<td>$location.path(path)</td>
</tr>
<tr>
<td>$location.hashSearch = search</td>
<td>$location.search(search)</td>
</tr>
<tr class="head">
<th>Navigation outside the app</td>
<th>Use lower level API</td>
</tr>
<tr>
<td>$location.href = value<br />$location.update(value)</td>
<td>$window.location.href = value</td>
</tr>
<tr>
<td>$location[protocol | host | port | path | search]</td>
<td>$window.location[protocol | host | port | path | search]</td>
</tr>
<tr class="head">
<th>Read access</td>
<th>Change to</td>
</tr>
<tr>
<td>$location.hashPath</td>
<td>$location.path()</td>
</tr>
<tr>
<td>$location.hashSearch</td>
<td>$location.search()</td>
</tr>
<tr>
<td>$location.href<br />$location.protocol<br />$location.host<br />$location.port<br
/>$location.hash</td>
<td>$location.absUrl()<br />$location.protocol()<br />$location.host()<br />$location.port()<br
/>$location.path() + $location.search()</td>
</tr>
<tr>
<td>$location.path<br />$location.search</td>
<td>$window.location.path<br />$window.location.search</td>
</tr>
</tbody>
</table>
## Two-way binding to $location
Because `$location` uses getters/setters, you can use `ng-model-options="{ getterSetter: true }"`
@@ -800,6 +884,6 @@ angular.module('locationExample', [])
</file>
</example>
## Related API
# Related API
* {@link ng.$location `$location` API}
+11 -14
View File
@@ -222,26 +222,23 @@ triggered:
| Directive | Supported Animations |
|-------------------------------------------------------------------------------|---------------------------------------------------------------------------|
| {@link ng.directive:form#animations form / ngForm} | add and remove ({@link ng.directive:form#css-classes various classes}) |
| {@link ngAnimate.directive:ngAnimateSwap#animations ngAnimateSwap} | enter and leave |
| {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave, and move |
| {@link ng.directive:ngIf#animations ngIf} | enter and leave |
| {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
| {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
| {@link ngRoute.directive:ngView#animations ngView} | enter and leave |
| {@link module:ngMessages#animations ngMessage / ngMessageExp} | enter and leave |
| {@link ng.directive:ngClass#animations ngClass / {{class&#125;&#8203;&#125;} | add and remove |
| {@link ng.directive:ngClassEven#animations ngClassEven} | add and remove |
| {@link ng.directive:ngClassOdd#animations ngClassOdd} | add and remove |
| {@link ng.directive:ngHide#animations ngHide} | add and remove (the `ng-hide` class) |
| {@link ng.directive:ngIf#animations ngIf} | enter and leave |
| {@link ng.directive:ngInclude#animations ngInclude} | enter and leave |
| {@link module:ngMessages#animations ngMessage / ngMessageExp} | enter and leave |
| {@link module:ngMessages#animations ngMessages} | add and remove (the `ng-active`/`ng-inactive` classes) |
| {@link ng.directive:ngModel#animations ngModel} | add and remove ({@link ng.directive:ngModel#css-classes various classes}) |
| {@link ng.directive:ngRepeat#animations ngRepeat} | enter, leave, and move |
| {@link ng.directive:ngShow#animations ngShow} | add and remove (the `ng-hide` class) |
| {@link ng.directive:ngSwitch#animations ngSwitch} | enter and leave |
| {@link ngRoute.directive:ngView#animations ngView} | enter and leave |
(More information can be found by visiting the documentation associated with each directive.)
| {@link ng.directive:ngModel#animations ngModel} | add and remove ({@link ng.directive:ngModel#css-classes various classes}) |
| {@link ng.directive:form#animations form / ngForm} | add and remove ({@link ng.directive:form#css-classes various classes}) |
| {@link module:ngMessages#animations ngMessages} | add and remove (the `ng-active`/`ng-inactive` classes) |
For a full breakdown of the steps involved during each animation event, refer to the
{@link ng.$animate `$animate` API docs}.
{@link ng.$animate API docs}.
## How do I use animations in my own directives?
@@ -471,7 +468,7 @@ You can also use one of the other
strategies to disable animations}.
## Enable animations outside of the application DOM tree: {@link ng.$animate#pin $animate.pin()}
### Enable animations for elements outside of the AngularJS application DOM tree: {@link ng.$animate#pin $animate.pin()}
Before animating, `ngAnimate` checks if the animated element is inside the application DOM tree. If
not, no animation is run. Usually, this is not a problem since most apps use the `html` or `body`
@@ -14,7 +14,6 @@ This guide describes the Component Router for AngularJS.
<div class="alert alert-info">
If you are looking for information about the default router for AngularJS have a look at the {@link ngRoute} module.
If you are looking for information about the Component Router for the new Angular then
check out the [Angular Router Guide](https://angular.io/docs/ts/latest/guide/router.html).
</div>
+2 -2
View File
@@ -25,7 +25,7 @@ When not to use Components:
## Creating and configuring a Component
Components can be registered using the {@link ng.$compileProvider#component `.component()`} method of an AngularJS module (returned by {@link module `angular.module()`}). The method takes two arguments:
Components can be registered using the `.component()` method of an AngularJS module (returned by {@link module `angular.module()`}). The method takes two arguments:
* The name of the Component (as string).
* The Component config object. (Note that, unlike the `.directive()` method, this method does **not** take a factory function.)
@@ -143,7 +143,7 @@ components should follow a few simple conventions:
}
```
- **Components have a well-defined lifecycle:**
- **Components have a well-defined lifecycle**
Each component can implement "lifecycle hooks". These are methods that will be called at certain points in the life
of the component. The following hook methods can be implemented:
+2 -1
View File
@@ -116,6 +116,7 @@ objects (or primitives) assigned to the scope become model properties. Any metho
the scope are available in the template/view, and can be invoked via AngularJS expressions
and `ng` event handler directives (e.g. {@link ng.directive:ngClick ngClick}).
## Simple Spicy Controller Example
To illustrate further how Controller components work in AngularJS, let's create a little app with the
@@ -156,7 +157,7 @@ string "very". Depending on which button is clicked, the `spice` model is set to
Things to notice in the example above:
- The `ng-controller` directive is used to (implicitly) create a scope for our template, and the
- The `ngController` directive is used to (implicitly) create a scope for our template, and the
scope is augmented (managed) by the `SpicyController` Controller.
- `SpicyController` is just a plain JavaScript function. As an (optional) naming convention the name
starts with capital letter and ends with "Controller".
+2 -2
View File
@@ -125,8 +125,8 @@ and comments (M).
The built-in AngularJS directives show in their documentation page which type of matching they support.
The following demonstrates the various ways a directive (`myDir` in this case) that matches all
4 types can be referenced from within a template.
The following demonstrates the various ways a directive that matches all 4 types
(`myDir` in this case) can be referenced from within a template.
```html
<my-dir></my-dir>
+6
View File
@@ -5,6 +5,12 @@
# E2E Testing
<div class="alert alert-danger">
**Note:** In the past, end-to-end testing could be done with a deprecated tool called
[AngularJS Scenario Runner](http://code.angularjs.org/1.2.16/docs/guide/e2e-testing). That tool
is now in maintenance mode, and will be removed in version 1.7.0.
</div>
As applications grow in size and complexity, it becomes unrealistic to rely on manual testing to
verify the correctness of new features, catch bugs and notice regressions. Unit tests
are the first line of defense for catching bugs, but sometimes issues come up with integration
+2 -2
View File
@@ -37,8 +37,8 @@ AngularJS expressions are like JavaScript expressions with the following differe
even inside `ng-init` directive.
* **No RegExp Creation With Literal Notation:** You cannot create regular expressions
in an AngularJS expression. An exception to this rule is {@link ngPattern `ng-pattern`} which accepts valid
RegExp.
in an AngularJS expression. An exception to this rule is {@link ngPattern `ng-pattern`} which
accepts valid RegExp.
* **No Object Creation With New Operator:** You cannot use `new` operator in an AngularJS expression.
+1 -1
View File
@@ -75,7 +75,7 @@ Official announcements, news and releases are posted to our blog, G+ and Twitter
* [AngularJS Blog](http://blog.angularjs.org/)
* [Google+](https://plus.google.com/u/0/+AngularJS)
* [Twitter](https://twitter.com/angular)
* [Twitter](https://twitter.com/angularjs)
* [AngularJS on YouTube](http://youtube.com/angularjs)
## Contributing to AngularJS
+3 -807
View File
@@ -15,810 +15,6 @@ which drives many of these changes.
* Several new features, especially animations, would not be possible without a few changes.
* Finally, some outstanding bugs were best fixed by changing an existing API.
## Migrating from 1.6 to 1.7
AngularJS 1.7 contains bug fixes and features to AngularJS core and its external modules, some of
which contain breaking changes. However, most of these address internal behavior and not APIs, and
should not affect many applications.
Additionally, we have removed some long-deprecated modules and APIs.
The most notable changes are:
- `$resource` has now support for request and requestError interceptors
- Several deprecated features have been removed:
- the `$controllerProvider.allowGlobals()` flag
- the `$compileProvider.preAssignBindingsEnabled()` flag
- the `angular.lowercase` and `angular.uppercase` methods
- the `$cookieStore` service from the `ngCookies` module
- the `ngClick` override directive and corresponding services from the `ngTouch` module
- the complete `ngScenario` module
Please note that feature development (without breaking changes) has happened in parallel on the
1.6.x branch, so 1.7 doesn't contain many new features, but you may still benefit from those
features that were added (with possible BCs), bugfixes, and a few smaller performance improvements.
<br />
Below is the full list of breaking changes:
<br />
<a name="migrate1.6to1.7-ng-directives"></a>
### Core: _Directives_
#### **form**
**Due to [223de5](https://github.com/angular/angular.js/commit/223de59e988dc0cc8b4ec3a045b7c0735eba1c77)**,
forms will now set `$submitted` on child forms when they are submitted.
For example:
```
<form name="parentform" ng-submit="$ctrl.submit()">
<ng-form name="childform">
<input type="text" name="input" ng-model="my.model" />
</ng-form>
<input type="submit" />
</form>
```
Submitting this form will set `$submitted` on "parentform" and "childform".
Previously, it was only set on "parentform".
This change was introduced because mixing `form` and `ngForm` does not create
logically separate forms, but rather something like input groups.
Therefore, child forms should inherit the submission state from their parent form.
#### **input[radio]** and **input[checkbox]**
**Due to [656c8f](https://github.com/angular/angular.js/commit/656c8fa8f23b1277cc5c214c4d0237f3393afa1e)**,
`input[radio]` and `input[checkbox]` now listen to the "change" event instead of the "click" event.
Most apps should not be affected, as "change" is automatically fired by browsers after "click"
happens.
Two scenarios might need migration:
- Custom click events:
Before this change, custom click event listeners on radio / checkbox would be called after the
input element and `ngModel` had been updated, unless they were specifically registered before
the built-in click handlers.
After this change, they are called before the input is updated, and can call
`event.preventDefault()` to prevent the input from updating.
If an app uses a click event listener that expects `ngModel` to be updated when it is called, it now
needs to register a change event listener instead.
- Triggering click events:
Conventional trigger functions:
The change event might not be fired when the input element is not attached to the document. This
can happen in **tests** that compile input elements and trigger click events on them. Depending on
the browser (Chrome and Safari) and the trigger method, the change event will not be fired when the
input isn't attached to the document.
Before:
```js
it('should update the model', inject(function($compile, $rootScope) {
var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);
inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
expect($rootScope.checkbox).toBe(true);
});
```
With this patch, `$rootScope.checkbox` might not be true, because the click event hasn't triggered
the change event. To make the test, work append `inputElm` to the app's `$rootElement`, and the
`$rootElement` to the `$document`.
After:
```js
it('should update the model', inject(function($compile, $rootScope, $rootElement, $document) {
var inputElm = $compile('<input type="checkbox" ng-model="checkbox" />')($rootScope);
$rootElement.append(inputElm);
$document.append($rootElement);
inputElm[0].click(); // Or different trigger mechanisms, such as jQuery.trigger()
expect($rootScope.checkbox).toBe(true);
});
```
#### **input\[number\]**
**Due to [aa3f95](https://github.com/angular/angular.js/commit/aa3f951330ec7b10b43ea884d9b5754e296770ec)**,
`input[type=number]` with `ngModel` now validates the input for the `max`/`min` restriction against
the `ngModelController.$viewValue` instead of against the `ngModelController.$modelValue`.
This affects apps that use `$parsers` or `$formatters` to transform the input / model value.
If you rely on the `$modelValue` validation, you can overwrite the `min`/`max` validator from a
custom directive, as seen in the following example directive definition object:
```js
{
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
var maxValidator = ctrl.$validators.max;
ctrk.$validators.max = function(modelValue, viewValue) {
return maxValidator(modelValue, modelValue);
};
}
}
```
#### **ngModel, input**
**Due to [74b04c](https://github.com/angular/angular.js/commit/74b04c9403af4fc7df5b6420f22c9f45a3e84140)**,
*Custom* parsers that fail to parse on input types "email", "url", "number", "date", "month",
"time", "datetime-local", "week", no longer set `ngModelController.$error[inputType]`, and
the `ng-invalid-[inputType]` class. Also, custom parsers on input type "range" no longer set `ngModelController.$error.number` and the `ng-invalid-number` class.
Instead, any custom parsers on these inputs set `ngModelController.$error.parse` and
`ng-invalid-parse`. This change was made to make distinguishing errors from built-in parsers
and custom parsers easier.
#### **ngModelOptions**
**Due to [55ba44](https://github.com/angular/angular.js/commit/55ba44913e02650b56410aa9ab5eeea5d3492b68)**,
the 'default' key in 'debounce' now only debounces the default event, i.e. the event that is added
as an update trigger by the different input directives automatically.
Previously, it also applied to other update triggers defined in 'updateOn' that
did not have a corresponding key in the 'debounce'.
This behavior is now supported via a special wildcard / catch-all key: '*'.
See the following example:
Pre-1.7:
'mouseup' is also debounced by 500 milliseconds because 'default' is applied:
```html
ng-model-options="{
updateOn: 'default blur mouseup',
debounce: { 'default': 500, 'blur': 0 }
}"
```
1.7:
The pre-1.7 behavior can be re-created by setting '*' as a catch-all debounce value:
```html
ng-model-options="{
updateOn: 'default blur mouseup',
debounce: { '*': 500, 'blur': 0 }
}"
```
In contrast, when only 'default' is used, 'blur' and 'mouseup' are not debounced:
```html
ng-model-options="{
updateOn: 'default blur mouseup',
debounce: { 'default': 500 }
}
```
#### **ngStyle**
**Due to [15bbd3](https://github.com/angular/angular.js/commit/15bbd3e18cd89b91f7206a06c73d40e54a8a48a0)**,
the use of deep-watching in `ngStyle` has changed. Previously, `ngStyle` would trigger styles to be
re-applied whenever nested state changed. Now, only changes to direct properties of the watched
object will trigger changes.
<a name="migrate1.6to1.7-ng-services"></a>
### Core: _Services_
#### **$compile**
**Due to [38f8c9](https://github.com/angular/angular.js/commit/38f8c97af74649ce224b6dd45f433cc665acfbfb)**,
directive bindings are no longer available in the constructor.
Previously, the `$compileProvider.preAssignBindingsEnabled` flag was supported.
The flag controlled whether bindings were available inside the controller
constructor or only in the `$onInit` hook. The bindings are now no longer
available in the constructor.
To migrate your code:
1. If you haven't invoked `$compileProvider.preAssignBindingsEnabled()` you
don't have to do anything to migrate.
2. If you specified `$compileProvider.preAssignBindingsEnabled(false)`, you
can remove that statement - since AngularJS 1.6.0 this is the default so your
app should still work even in AngularJS 1.6 after such removal. Afterwards,
migrating to AngularJS 1.7.0 shouldn't require any further action.
3. If you specified `$compileProvider.preAssignBindingsEnabled(true)` you need
to first migrate your code so that the flag can be flipped to `false`. The
instructions on how to do that are available in the "Migrating from 1.5 to 1.6"
guide: https://docs.angularjs.org/guide/migration#migrating-from-1-5-to-1-6
Afterwards, remove the `$compileProvider.preAssignBindingsEnabled(true)`
statement.
<hr />
**Due to [6ccbfa](https://github.com/angular/angular.js/commit/6ccbfa65d60a3dc396d0cf6da21b993ad74653fd)**,
the `xlink:href` security context for SVG's `a` and `image` elements has been lowered.
In the unlikely case that an app relied on `RESOURCE_URL` whitelisting for the
purpose of binding to the `xlink:href` property of SVG's `<a>` or `<image>`
elements and if the values do not pass the regular URL sanitization, they will
break.
To fix this you need to ensure that the values used for binding to the affected
`xlink:href` contexts are considered safe URLs, e.g. by whitelisting them in
`$compileProvider`'s `aHrefSanitizationWhitelist` (for `<a>` elements) or
`imgSrcSanitizationWhitelist` (for `<image>` elements).
<hr />
**Due to [fd4f01](https://github.com/angular/angular.js/commit/fd4f0111188b62773b99ab6eab38b4d2b5d8d727)**,
deep-watching is no longer used in literal one-way bindings.
Previously, when a literal value was passed into a directive/component via
one-way binding it would be watched with a deep watcher.
For example, for `<my-component input="[a]">`, a new instance of the array
would be passed into the directive/component (and trigger `$onChanges`) not
only if `a` changed but also if any sub property of `a` changed such as
`a.b` or `a.b.c.d.e` etc.
This also means a new but equal value for `a` would NOT trigger such a
change.
Now, literal values use an input-based watch similar to other directive/component
one-way bindings. In this context inputs are the non-constant parts of the
literal. In the example above, the input would be `a`. Changes are only
triggered, when the inputs to the literal change.
<hr />
**Due to [1cf728](https://github.com/angular/angular.js/commit/1cf728e209a9e0016068fac2769827e8f747760e)**,
`base[href]` was added to the list of `RESOURCE_URL` context attributes.
Previously, `<base href="{{ $ctrl.baseUrl }}" />` would not require `baseUrl` to
be trusted as a `RESOURCE_URL`. Now, `baseUrl` will be sent to `$sce`'s
`RESOURCE_URL` checks. By default, it will break unless `baseUrl` is of the same
origin as the application document.
Refer to the
[`$sce` API docs](https://code.angularjs.org/snapshot/docs/api/ng/service/$sce)
for more info on how to trust a value in a `RESOURCE_URL` context.
Also, concatenation in trusted contexts is not allowed, which means that the
following won't work: `<base href="/something/{{ $ctrl.partialPath }}" />`.
Either construct complex values in a controller (recommended):
```js
this.baseUrl = '/something/' + this.partialPath;
```
```html
<base href="{{ $ctrl.baseUrl }}" />
```
Or use string concatenation in the interpolation expression (not recommended
except for the simplest of cases):
```html
<base href="{{ '/something/' + $ctrl.partialPath }}" />
```
#### **$rootScope**
**Due to ([c2b8fa](https://github.com/angular/angular.js/commit/c2b8fab0a480204374d561d6b9b3d47347ac5570))**,
the arguments of `$watchGroup` callbacks have changed.
Previously, when using `$watchGroup`, the entries in `newValues` and
`oldValues` represented the *most recent change of each entry*.
Now, the entries in `oldValues` will always equal the `newValues` of the previous
call of the listener. This means comparing the entries in `newValues` and
`oldValues` can be used to determine which individual expressions changed.
For example `$scope.$watchGroup(['a', 'b'], fn)` would previously:
| Action | newValue | oldValue |
|----------|------------|------------|
| (init) | [undefined, undefined] | [undefined, undefined] |
| `a=1` | [1, undefined] | [undefined, undefined] |
| `a=2` | [2, undefined] | [1, undefined] |
| `b=3` | [2, 3] | [1, undefined] |
Now the `oldValue` will always equal the previous `newValue`:
| Action | newValue | oldValue |
|----------|------------|------------|
| (init) | [undefined, undefined] | [undefined, undefined] |
| `a=1` | [1, undefined] | [undefined, undefined] |
| `a=2` | [2, undefined] | [1, undefined] |
| `b=3` | [2, 3] | [2, undefined] |
Note the last call now shows `a === 2` in the `oldValues` array.
This also makes the `oldValue` of one-time watchers more clear. Previously,
the `oldValue` of a one-time watcher would remain `undefined` forever. For
example `$scope.$watchGroup(['a', '::b'], fn)` would previously:
| Action | newValue | oldValue |
|----------|------------|------------|
| (init) | [undefined, undefined] | [undefined, undefined] |
| `a=1` | [1, undefined] | [undefined, undefined] |
| `b=2` | [1, 2] | [undefined, undefined] |
| `a=b=3` | [3, 2] | [1, undefined] |
Where now the `oldValue` will always equal the previous `newValue`:
| Action | newValue | oldValue |
|----------|------------|------------|
| (init) | [undefined, undefined] | [undefined, undefined] |
| `a=1` | [1, undefined] | [undefined, undefined] |
| `b=2` | [1, 2] | [1, undefined] |
| `a=b=3` | [3, 2] | [1, 2] |
#### **$interval**
**Due to [a8bef9](https://github.com/angular/angular.js/commit/a8bef95127775d83d80daa4617c33227c4b443d4)**,
`$interval.cancel()` will throw an error if called with a promise that was not generated by
`$interval()`. Previously, it would silently do nothing.
Before:
```js
var promise = $interval(doSomething, 1000, 5).then(doSomethingElse);
$interval.cancel(promise); // No error; interval NOT canceled.
```
After:
```js
var promise = $interval(doSomething, 1000, 5).then(doSomethingElse);
$interval.cancel(promise); // Throws error.
```
Correct usage:
```js
var promise = $interval(doSomething, 1000, 5);
var newPromise = promise.then(doSomethingElse);
$interval.cancel(promise); // Interval canceled.
```
#### **$timeout**
**Due to [336525](https://github.com/angular/angular.js/commit/3365256502344970f86355d3ace1cb4251ae9828)**,
`$timeout.cancel()` will throw an error if called with a promise that was not generated by
`$timeout()`. Previously, it would silently do nothing.
Before:
```js
var promise = $timeout(doSomething, 1000).then(doSomethingElse);
$timeout.cancel(promise); // No error; timeout NOT canceled.
```
After:
```js
var promise = $timeout(doSomething, 1000).then(doSomethingElse);
$timeout.cancel(promise); // Throws error.
```
Correct usage:
```js
var promise = $timeout(doSomething, 1000);
var newPromise = promise.then(doSomethingElse);
$timeout.cancel(promise); // Timeout canceled.
```
#### **$cookies**
**Due to [73c646](https://github.com/angular/angular.js/commit/73c6467f1468353215dc689c019ed83aa4993c77)**,
the `$cookieStore`service has been removed. Migrate to the `$cookies` service. Note that
for object values you need to use the `putObject` & `getObject` methods, as
`get`/`put` will not correctly save/retrieve the object values.
Before:
```js
$cookieStore.put('name', {key: 'value'});
$cookieStore.get('name'); // {key: 'value'}
$cookieStore.remove('name');
```
#### **$templateRequest**
**Due to [c617d6](https://github.com/angular/angular.js/commit/c617d6dceee5b000bfceda44ced22fc16b48b18b)**,
the `tpload` error namespace has changed. Previously, the `tpload` error was namespaced to
`$compile`. If you have code that matches errors of the form `[$compile:tpload]` it will no longer
run. You should change the code to match `[$templateRequest:tpload]`.
<hr />
**Due to ([fb0099](https://github.com/angular/angular.js/commit/fb00991460cf69ae8bc7f1f826363d09c73c0d5e)**,
`$templateRequest()` now returns the result of `$templateCache.put()` when making a server request
for a template. Previously, it would return the content of the response directly.
This means that if you are decorating `$templateCache.put()` to manipulate the template, you will
now get this manipulated result also on the first `$templateRequest()` call rather than only on
subsequent calls (when the template is retrieved from the cache).
In practice, this should not affect any apps, as it is unlikely that they rely on the template being
different in the first and subsequent calls.
#### **$animate**
**Due to [16b82c](https://github.com/angular/angular.js/commit/16b82c6afe0ab916fef1d6ca78053b00bf5ada83)**,
`$animate.cancel(runner)` now rejects the underlying promise and calls the `catch()` handler on the
runner returned by `$animate` functions (`enter`, `leave`, `move`, `addClass`, `removeClass`,
`setClass`, `animate`).
Previously, it would resolve the promise as if the animation had ended successfully.
Example:
```js
var runner = $animate.addClass('red');
runner.then(function() { console.log('success')});
runner.catch(function() { console.log('cancelled')});
runner.cancel();
```
Pre-1.7.0, this logs 'success', 1.7.0 and later it logs 'cancelled'.
To migrate, add a `catch()` handler to your animation runners.
#### **$controller**
**Due to [e269c1](https://github.com/angular/angular.js/commit/e269c14425a3209040f65c022658770e00a36f16)**,
the option to instantiate controllers from constructors on the global `window` object
has been removed. Likewise, the deprecated `$controllerProvider.allowGlobals()`
method that could enable this behavior, has been removed.
This behavior had been deprecated since AngularJS v1.3.0, because polluting the global scope
is considered bad practice. To migrate, remove the call to `$controllerProvider.allowGlobals()` in
the config, and register your controller via the Module API or the `$controllerProvider`, e.g.:
```js
angular.module('myModule', []).controller('myController', function() {...});
// or
angular.module('myModule', []).config(function($controllerProvider) {
$controllerProvider.register('myController', function() {...});
});
```
#### **$sce**
**Due to [1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)**,
if you use `attrs.$set` for URL attributes (`a[href]` and `img[src]`) there will no
longer be any automated sanitization of the value. This is in line with other
programmatic operations, such as writing to the `innerHTML` of an element.
If you are programmatically writing URL values to attributes from untrusted
input, then you must sanitize it yourself. You could write your own sanitizer or copy
the private `$$sanitizeUri` service.
Note that values that have been passed through the `$interpolate` service within the
`URL` or `MEDIA_URL` will have already been sanitized, so you would not need to sanitize
these values again.
<hr/>
**Due to [1e9ead](https://github.com/angular/angular.js/commit/1e9eadcd72dbbd5c67dae8328a63e535cfa91ff9)**,
binding {@link ng.$sce#trustAs trustAs()} and the short versions
({@link ng.$sce#trustAsResourceUrl trustAsResourceUrl()} et al.) to {@link ng.ngSrc},
{@link ng.ngSrcset}, and {@link ng.ngHref} will now raise an infinite digest error:
```js
$scope.imgThumbFn = function(id) {
return $sce.trustAsResourceUrl(someService.someUrl(id));
};
```
```html
<img ng-src="{{ imgThumbFn(imgId) }}" />
```
This is because {@link ng.$interpolate} is now responsible for sanitizing
the attribute value, and its watcher receives a new object from `trustAs()`
on every digest.
To migrate, compute the trusted value only when the input value changes:
```js
$scope.$watch('imgId', function(id) {
$scope.imgThumb = $sce.trustAsResourceUrl(someService.someUrl(id));
});
```
```html
<img ng-src="{{ imgThumb }}" />
```
<a name="migrate1.6to1.7-ng-filters"></a>
### Core: _Filters_
#### **orderBy**
**Due to [1d8046](https://github.com/angular/angular.js/commit/1d804645f7656d592c90216a0355b4948807f6b8)**,
when using `orderBy` to sort arrays containing `null` values, the `null` values
will be considered "greater than" all other values, except for `undefined`.
Previously, they were sorted as strings. This will result in different (but more
intuitive) sorting order.
Before:
```js
orderByFilter(['a', undefined, 'o', null, 'z']);
//--> 'a', null, 'o', 'z', undefined
```
After:
```js
orderByFilter(['a', undefined, 'o', null, 'z']);
//--> 'a', 'o', 'z', null, undefined
```
<a name="migrate1.6to1.7-ng-misc"></a>
### Core: _Misceallenous_
#### **jqLite**
**Due to [b7d396](https://github.com/angular/angular.js/commit/b7d396b8b6e8f27a1f4556d58fc903321e8d532a)**,
`removeData()` no longer removes event handlers.
Before this commit `removeData()` invoked on an element removed its event
handlers as well. If you want to trigger a full cleanup of an element, change:
```js
elem.removeData();
```
to:
```js
angular.element.cleanData(elem);
```
In most cases, though, cleaning up after an element is supposed to be done
only when it's removed from the DOM as well; in such cases the following:
```js
elem.remove();
```
will remove event handlers as well.
#### **Helpers**
**Due to [1daa4f](https://github.com/angular/angular.js/commit/1daa4f2231a89ee88345689f001805ffffa9e7de)**,
the helper functions `angular.lowercase` and `angular.uppercase` have been removed.
These functions have been deprecated since 1.5.0. They are internally
used, but should not be exposed as they contain special locale handling
(for Turkish) to maintain internal consistency regardless of user-set locale.
Developers should generally use the built-in methods `toLowerCase` and `toUpperCase`
or `toLocaleLowerCase` and `toLocaleUpperCase` for special cases.
<hr />
**Due to [e3ece2](https://github.com/angular/angular.js/commit/e3ece2fad9e1e6d47b5f06815ff186d7e6f44948)**,
`angular.isArray()` now supports Array subclasses.
Previously, `angular.isArray()` was an alias for `Array.isArray()`.
Therefore, objects that prototypally inherit from `Array` where not
considered arrays. Now such objects are considered arrays too.
This change affects several other methods that use `angular.isArray()`
under the hood, such as `angular.copy()`, `angular.equals()`,
`angular.forEach()`, and `angular.merge()`.
This in turn affects how dirty checking treats objects that prototypally
inherit from `Array` (e.g. MobX observable arrays). AngularJS will now
be able to handle these objects better when copying or watching.
<a name="migrate1.6to1.7-ngAria"></a>
### ngAria
**Due to [6d5ef3](https://github.com/angular/angular.js/commit/6d5ef34fc6a974cde73157ba94f9706723dd8f5b)**,
`ngAria` no longer sets `aria-*` attributes on `input[type="hidden"]` with `ngModel`.
This can affect apps that test for the presence of ARIA attributes on hidden inputs.
To migrate, remove these assertions.
In actual apps, this should not have a user-facing effect, as the previous behavior
was incorrect, and the new behavior is correct for accessibility.
<a name="migrate1.6to1.7-ngResource"></a>
### ngResource
#### **$resource**
**Due to [ea0585](https://github.com/angular/angular.js/commit/ea0585773bb93fd891576e2271254a17e15f1ddd)**,
the behavior of interceptors and success/error callbacks has changed.
If you are not using `success` or `error` callbacks with `$resource`,
your app should not be affected by this change.
If you are using `success` or `error` callbacks (with or without
response interceptors), one (subtle) difference is that throwing an
error inside the callbacks will not propagate to the returned
`$promise`. Therefore, you should try to use the promises whenever
possible. E.g.:
```js
// Avoid
User.query(function onSuccess(users) { throw new Error(); }).
$promise.
catch(function onError() { /* Will not be called. */ });
// Prefer
User.query().
$promise.
then(function onSuccess(users) { throw new Error(); }).
catch(function onError() { /* Will be called. */ });
```
Finally, if you are using `success` or `error` callbacks with response
interceptors, the callbacks will now always run _after_ the interceptors
(and wait for them to resolve in case they return a promise).
Previously, the `error` callback was called before the `responseError`
interceptor and the `success` callback was synchronously called after
the `response` interceptor. E.g.:
```js
var User = $resource('/api/users/:id', {id: '@id'}, {
get: {
method: 'get',
interceptor: {
response: function(response) {
console.log('responseInterceptor-1');
return $timeout(1000).then(function() {
console.log('responseInterceptor-2');
return response.resource;
});
},
responseError: function(response) {
console.log('responseErrorInterceptor-1');
return $timeout(1000).then(function() {
console.log('responseErrorInterceptor-2');
return $q.reject('Ooops!');
});
}
}
}
});
var onSuccess = function(value) { console.log('successCallback', value); };
var onError = function(error) { console.log('errorCallback', error); };
// Assuming the following call is successful...
User.get({id: 1}, onSuccess, onError);
// Old behavior:
// responseInterceptor-1
// successCallback, {/* Promise object */}
// responseInterceptor-2
// New behavior:
// responseInterceptor-1
// responseInterceptor-2
// successCallback, {/* User object */}
// Assuming the following call returns an error...
User.get({id: 2}, onSuccess, onError);
// Old behavior:
// errorCallback, {/* Response object */}
// responseErrorInterceptor-1
// responseErrorInterceptor-2
// New behavior:
// responseErrorInterceptor-1
// responseErrorInterceptor-2
// errorCallback, Ooops!
```
<hr />
**Due to [240a3d](https://github.com/angular/angular.js/commit/240a3ddbf12a9bb79754031be95dae4b6bd2dded)**,
`$http` will be called asynchronously from `$resource` methods
(regardless if a `request`/`requestError` interceptor has been defined).
Previously, calling a `$resource` method would synchronously call
`$http`.
This is not expected to affect applications at runtime, since the
overall operation is asynchronous already, but may affect assertions in
tests. For example, if you want to assert that `$http` has been called
with specific arguments as a result of a `$resource` call, you now need
to run a `$digest` first, to ensure the (possibly empty) request
interceptor promise has been resolved.
Before:
```js
it('...', function() {
$httpBackend.expectGET('/api/things').respond(...);
var Things = $resource('/api/things');
Things.query();
expect($http).toHaveBeenCalledWith(...);
});
```
After:
```js
it('...', function() {
$httpBackend.expectGET('/api/things').respond(...);
var Things = $resource('/api/things');
Things.query();
$rootScope.$digest();
expect($http).toHaveBeenCalledWith(...);
});
```
<a name="migrate1.6to1.7-ngScenario"></a>
### ngScenario
**Due to[0cd392](https://github.com/angular/angular.js/commit/0cd39217828b0ad53eaf731576af17d66c18ff60)**,
the angular scenario runner end-to-end test framework has been
removed from the project and will no longer be available on npm
or bower starting with 1.7.0.
It has been deprecated and removed from the documentation since 2014.
Applications that still use it should migrate to
[Protractor](http://www.protractortest.org).
Technically, it should also be possible to continue using an
older version of the scenario runner, as the underlying APIs have
not changed. However, we do not guarantee future compatibility.
<a name="migrate1.6to1.7-ngTouch"></a>
### ngTouch
**Due to [11d9ad](https://github.com/angular/angular.js/commit/11d9ad1eb25eaf5967195e424108207427835d50)**,
the `ngClick` directive of the `ngTouch` module has been removed, and with it the
corresponding `$touchProvider` and `$touch` service.
If you have included `ngTouch` v1.5.0 or higher in your application, and have not
changed the value of `$touchProvider.ngClickOverrideEnabled()`, or injected and used the `$touch`
service, then there are no migration steps for your code. Otherwise you must remove references to
the provider and service.
The `ngClick` override directive had been deprecated and by default disabled since v1.5.0,
because of buggy behavior in edge cases, and a general trend to avoid special touch based
overrides of click events. In modern browsers, it should not be necessary to use a touch override
library:
- Chrome, Firefox, Edge, and Safari remove the 300ms delay when
`<meta name="viewport" content="width=device-width">` is set.
- Internet Explorer 10+, Edge, Safari, and Chrome remove the delay on elements that have the
`touch-action` css property is set to `manipulation`.
You can find out more in these articles:
https://developers.google.com/web/updates/2013/12/300ms-tap-delay-gone-away
https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_9_1.html#//apple_ref/doc/uid/TP40014305-CH10-SW8
https://blogs.msdn.microsoft.com/ie/2015/02/24/pointer-events-w3c-recommendation-interoperable-touch-and-removing-the-dreaded-300ms-tap-delay/
## Migrating from 1.5 to 1.6
AngularJS 1.6 fixes numerous bugs and adds new features, both in core and in external modules.
@@ -2076,7 +1272,7 @@ Due to [b71d7c3f](https://github.com/angular/angular.js/commit/b71d7c3f3c04e65b0
falsy values (`''`, `0`, `false` and `null`) are properly recognized as option group identifiers for
options passed to `ngOptions`. Previously, all of these values were ignored and the option was not
assigned to any group. `undefined` is still interpreted as "no group".
If you have options with falsy group identifiers that should still not be assigned to any group,
If you have options with falsy group indentifiers that should still not be assigned to any group,
then you must filter the values before passing them to `ngOptions`, converting falsy values to
`undefined`.
@@ -2235,8 +1431,8 @@ For more info on the topic, you can take a look at this
## Migrating from 1.3 to 1.4
AngularJS 1.4 fixes major animation issues and introduces a new API for `ngCookies`. Further, there
are changes to `ngMessages`, `$compile`, `ngRepeat`, `ngOptions`, `ngPattern`, `pattern` and some fixes to core filters:
`limitTo` and `filter`.
are changes to `ngMessages`, `$compile`, `ngRepeat`, `ngOptions`, `ngPattern`, `pattern`, and
some fixes to core filters: `limitTo` and `filter`.
The reason for the ngAnimate refactor was to fix timing issues and to expose new APIs to allow
for developers to construct more versatile animations. We now have access to `$animateCss`
+5
View File
@@ -188,6 +188,11 @@ myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);
Much simpler!
Note: Yes, we have called one of our service recipes 'Service'. We regret this and know that we'll
be somehow punished for our misdeed. It's like we named one of our offspring 'Child'. Boy,
that would mess with the teachers.
## Provider Recipe
As already mentioned in the intro, the Provider recipe is the core recipe type and
+12 -21
View File
@@ -109,14 +109,12 @@ to test the behavior without being distracted by the rendering details.
## Scope Hierarchies
Each AngularJS application has exactly one {@link ng.$rootScope root scope}, but
may have any number of child scopes.
may have several child scopes.
The application can have multiple scopes, because {@link guide/directive directives} can create new
child scopes. When new scopes are created, they are added as children of their parent scope. This
creates a tree structure which parallels the DOM where they're attached.
The section {@link guide/scope#directives-that-create-scopes Directives that Create Scopes} has more
info about which directives create scopes.
The application can have multiple scopes, because some {@link guide/directive directives} create
new child scopes (refer to directive documentation to see which directives create new scopes).
When new scopes are created, they are added as children of their parent scope. This creates a tree
structure which parallels the DOM where they're attached.
When AngularJS evaluates `{{name}}`, it first looks at the scope associated with the given
element for the `name` property. If no such property is found, it searches the parent scope
@@ -185,11 +183,8 @@ To examine the scope in the debugger:
2. The debugger allows you to access the currently selected element in the console as `$0`
variable.
3. To retrieve the associated scope in console execute: `angular.element($0).scope()`
3. To retrieve the associated scope in console execute: `angular.element($0).scope()` or just type $scope
<div class="alert alert-warning">
The `scope()` function is only available when {@link ng.$compileProvider#debugInfoEnabled `$compileProvider.debugInfoEnabled()`} is true (which is the default).
</div>
## Scope Events Propagation
@@ -314,16 +309,12 @@ correctly.
In most cases, {@link ng.$compileProvider#directive directives} and scopes interact
but do not create new instances of scope. However, some directives, such as {@link
ng.directive:ngController ng-controller} and {@link ng.directive:ngRepeat ng-repeat},
create new child scopes and attach the child scope to the corresponding DOM element.
A special type of scope is the `isolate` scope, which does not inherit prototypically from the parent scope.
This type of scope is useful for component directives that should be isolated from their parent scope.
See the {@link guide/directive#isolating-the-scope-of-a-directive directives guide} for
more information about isolate scopes in custom directives.
Note also that component directives, which are created with the
{@link api/ng/type/angular.Module#component .component()} helper always create an isolate scope.
ng.directive:ngController ng-controller} and {@link
ng.directive:ngRepeat ng-repeat}, create new child scopes
and attach the child scope to the corresponding DOM element. You can retrieve a scope for any DOM
element by using an `angular.element(aDomElement).scope()` method call.
See the {@link guide/directive#isolating-the-scope-of-a-directive
directives guide} for more information about isolate scopes.
### Controllers and Scopes
+1 -1
View File
@@ -102,7 +102,7 @@ For more information please visit {@link $http#json-vulnerability-protection JSO
Bear in mind that calling `$http.jsonp` gives the remote server (and, if the request is not secured, any Man-in-the-Middle attackers)
instant remote code execution in your application: the result of these requests is handed off
to the browser as a regular `<script>` tag.
to the browser as regular `<script>` tag.
## Strict Contextual Escaping
+3 -4
View File
@@ -149,17 +149,16 @@ for instantiating controllers.
describe('PasswordController', function() {
beforeEach(module('app'));
var $controller, $rootScope;
var $controller;
beforeEach(inject(function(_$controller_, _$rootScope_){
beforeEach(inject(function(_$controller_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$controller = _$controller_;
$rootScope = _$rootScope_;
}));
describe('$scope.grade', function() {
it('sets the strength to "strong" if the password length is >8 chars', function() {
var $scope = $rootScope.$new();
var $scope = {};
var controller = $controller('PasswordController', { $scope: $scope });
$scope.password = 'longerthaneightchars';
$scope.grade();
+3 -2
View File
@@ -3,7 +3,8 @@
@description
# Including AngularJS scripts from the Google CDN
The quickest way to get started is to point your html `<script>` tag to a Google CDN URL.
The quickest way to get started is to point your html `<script>` tag to a
[Google CDN](https://developers.google.com/speed/libraries/#angularjs) URL.
This way, you don't have to download anything or maintain a local copy.
There are two types of AngularJS script URLs you can point to, one for development and one for
@@ -110,7 +111,7 @@ The set of files included in each version directory are:
* __`angular-route.js`__ — Routing and deep-linking services and directives for AngularJS apps.
({@link module:ngRoute API docs})
* __`angular-sanitize.js`__ — Functionality to sanitize HTML. ({@link module:ngSanitize API docs})
* __`angular-touch.js`__ — Touch events for touch-enabled devices.
* __`angular-touch.js`__ — Touch events and other helpers for touch-enabled devices.
({@link module:ngTouch API docs})
+4 -3
View File
@@ -8,7 +8,7 @@
### Why is this project called "AngularJS"? Why is the namespace called "ng"?
Because HTML has angular brackets and "ng" sounds like "AngularJS".
Because HTML has AngularJS brackets and "ng" sounds like "AngularJS".
### Is AngularJS a library, framework, plugin or a browser extension?
@@ -39,7 +39,7 @@ When we are making a release we generate updates to the changelog directly from
generated update contains a highlighted section that contains all the breaking changes that have been
extracted from the commits. We can quickly see in the new changelog exactly what commits contain breaking
changes and so can application developers when they are deciding whether to update to a new version of
AngularJS.
Angular.
Features with non-breaking changes can also appear in the "patch" version, e.g. in version 1.6.3 there might
be a feature that is not available in 1.6.2.
@@ -94,7 +94,8 @@ When adding new code to AngularJS, we have a very stringent commit policy:
- Commit messages must be written in a specific manner that allows us to parse them and extract the changes
for release notes ([see the contributing guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md))
The AngularJS code base has a very large set of unit tests and end-to-end tests. This means that a breaking change will require one or more tests to be changed to allow the
The AngularJS code base has a very large set of unit tests and end-to-end tests. This means that
a breaking change will require one or more tests to be changed to allow the
tests to pass. So when a commit includes tests that are being removed or modified, this is a flag that the
code might include a breaking change. When reviewing the commit we can then decide whether there really is
a breaking change and if it is appropriate for the branch to which it is being merged. If so, then we
+28 -21
View File
@@ -7,41 +7,48 @@
This page describes the support status of the significant versions of AngularJS.
<div class="alert alert-info">
On July 1, 2018 AngularJS entered a 3 year Long Term Support period.
AngularJS is planning one more significant release, version 1.7, and on July 1, 2018 it will enter a 3 year Long Term Support period.
</div>
### Until July 1st 2018
Any version branch not shown in the following table (e.g. 1.5.x) is no longer being developed.
<table class="dev-status table table-bordered">
<thead>
<tr><th>Version</th><th>Status</th><th>Comments</th></tr>
</thead>
<tbody>
<tr class="security"><td><span>1.2.x</span></td><td>Security patches only</td><td>Last version to provide IE 8 support</td></tr>
<tr class="stable"><td><span>1.6.x</span></td><td>Patch Releases</td><td>Minor features, bug fixes, security patches - no breaking changes</td></tr>
<tr class="current"><td><span>1.7.x</span></td><td>Active Development</td><td>1.7.0 (not yet released) will be the last release of AngularJS to contain breaking changes</td></tr>
</tbody>
</table>
### After July 1st 2018
Any version branch not shown in the following table (e.g. 1.6.x) is no longer being developed.
<table class="dev-status table table-bordered">
<thead>
<tr><th>Version</th><th>Status</th><th>Comments</th></tr>
</thead>
<tbody>
<tr class="security">
<td><span>1.2.x</span></td>
<td>Security patches only</td>
<td>Last version to provide IE 8 support</td>
</tr>
<tr class="stable">
<td><span>1.7.x</span></td>
<td>Long Term Support</td>
<td>See {@link version-support-status#long-term-support Long Term Support} section below.</td>
</tr>
</tbody>
<thead>
<tr><th>Version</th><th>Status</th><th>Comments</th></tr>
</thead>
<tbody>
<tr class="security"><td><span>1.2.x</span></td><td>Long Term Support</td><td>Last version to provide IE 8 support</td></tr>
<tr class="stable"><td><span>1.7.x</span></td><td>Long Term Support</td><td>See [Long Term Support](#long-term-support) section below.</td></tr>
</tbody>
</table>
### Long Term Support
On July 1st 2018, AngularJS entered a Long Term Support period for AngularJS.
On July 1st 2018, we will enter a Long Term Support period for AngularJS.
We now focus exclusively on providing fixes to bugs that satisfy at least one of the following criteria:
At this time we will focus exclusively on providing fixes to bugs that satisfy at least one of the following criteria:
* A security flaw is detected in the 1.7.x branch of the framework
* One of the major browsers releases a version that will cause current production applications using AngularJS 1.7.x to stop working
* The jQuery library releases a version that will cause current production applications using AngularJS 1.7.x to stop working.
AngularJS 1.2.x will get a new version if and only if a new severe security issue is discovered.
### Blog Post
You can read more about these plans in our [blog post announcement](https://blog.angular.io/stable-angularjs-and-long-term-support-7e077635ee9c).
You can read more about these plans in our [blog post announcement](https://blog.angular.io/stable-angularjs-and-long-term-support-7e077635ee9c).
+26 -8
View File
@@ -151,20 +151,38 @@ To learn more about AngularJS scopes, see the {@link ng.$rootScope.Scope Angular
### Testing Controllers
The "AngularJS way" of separating the controller from the view makes it easy to test code as it is being
developed. In the section "Model and Controller" we have registered our controller via a constructor
function on the `phonecatApp` module.
The "AngularJS way" of separating controller from the view, makes it easy to test code as it is being
developed. If our controller were available on the global namespace, we could simply instantiate it
with a mock scope object:
In tests, we use an AngularJS service, `$controller`, which will retrieve a controller by name. It
also takes a second argument - a map of dependencies that should be injected.
<br />
```js
describe('PhoneListController', function() {
The following test instantiates `PhoneListController` with a mock scope object,
and verifies that the phones array property on the scope contains three records.
it('should create a `phones` model with 3 phones', function() {
var scope = {};
var ctrl = new PhoneListController(scope);
This example demonstrates how easy it is to create a unit test for
expect(scope.phones.length).toBe(3);
});
});
```
The test instantiates `PhoneListController` and verifies that the phones array property on the
scope contains three records. This example demonstrates how easy it is to create a unit test for
code in AngularJS. Since testing is such a critical part of software development, we make it easy to
create tests in AngularJS so that developers are encouraged to write them.
### Testing non-global Controllers
In practice, you will not want to have your controller functions in the global namespace. Instead,
you can see that we have registered it via a constructor function on the `phonecatApp` module.
In this case AngularJS provides a service, `$controller`, which will retrieve your controller by name.
Here is the same test using `$controller`:
<br />
**`app/app.spec.js`:**
+4 -6
View File
@@ -107,33 +107,31 @@ module.exports = function(config, specificOptions) {
'SL_iOS_10': {
base: 'SauceLabs',
browserName: 'iphone',
platform: 'OS X 10.12',
version: '10.3'
},
'SL_iOS_11': {
base: 'SauceLabs',
browserName: 'iphone',
platform: 'OS X 10.12',
version: '11.2'
version: '11'
},
'BS_Chrome': {
base: 'BrowserStack',
browser: 'chrome',
os: 'OS X',
os_version: 'Sierra'
os_version: 'Yosemite'
},
'BS_Safari': {
base: 'BrowserStack',
browser: 'safari',
os: 'OS X',
os_version: 'Sierra'
os_version: 'Yosemite'
},
'BS_Firefox': {
base: 'BrowserStack',
browser: 'firefox',
os: 'Windows',
os_version: '10'
os_version: '8'
},
'BS_IE_9': {
base: 'BrowserStack',
+1 -1
View File
@@ -221,7 +221,7 @@ module.exports = {
//returns the 32-bit mode force flags for java compiler if supported, this makes the build much faster
java32flags: function() {
if (process.platform === 'win32') return '';
if (shell.exec('java -d32 -version 2>&1', {silent: true}).code !== 0) return '';
if (shell.exec('java -version -d32 2>&1', {silent: true}).code !== 0) return '';
return ' -d32 -client';
},
+1 -1
View File
@@ -11,7 +11,7 @@ set -e
# Curl and run this script as part of your .travis.yml before_script section:
# before_script:
# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash
SC_VERSION="4.4.12"
SC_VERSION="4.4.1"
CONNECT_URL="https://saucelabs.com/downloads/sc-$SC_VERSION-linux.tar.gz"
CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
CONNECT_DOWNLOAD="sc-$SC_VERSION-linux.tar.gz"
+14 -13
View File
@@ -1,9 +1,9 @@
{
"name": "angularjs",
"license": "MIT",
"branchVersion": "^1.7.0",
"branchPattern": "1.7.*",
"distTag": "latest",
"branchVersion": "^1.6.0",
"branchPattern": "1.6.*",
"distTag": "previous_1_6",
"repository": {
"type": "git",
"url": "https://github.com/angular/angular.js.git"
@@ -32,8 +32,8 @@
"commitplease": "^2.7.10",
"cross-spawn": "^4.0.0",
"cz-conventional-changelog": "1.1.4",
"dgeni": "^0.4.9",
"dgeni-packages": "^0.26.5",
"dgeni": "^0.4.0",
"dgeni-packages": "^0.16.4",
"eslint-plugin-promise": "^3.6.0",
"event-stream": "~3.1.0",
"glob": "^6.0.1",
@@ -63,18 +63,19 @@
"jquery": "3.2.1",
"jquery-2.1": "npm:jquery@2.1.4",
"jquery-2.2": "npm:jquery@2.2.4",
"karma": "^2.0.4",
"karma-browserstack-launcher": "^1.3.0",
"karma-chrome-launcher": "^2.2.0",
"karma": "^2.0.0",
"karma-browserstack-launcher": "^1.2.0",
"karma-chrome-launcher": "^2.1.1",
"karma-edge-launcher": "^0.4.2",
"karma-firefox-launcher": "^1.1.0",
"karma-firefox-launcher": "^1.0.1",
"karma-ie-launcher": "^1.0.0",
"karma-jasmine": "^1.1.2",
"karma-jasmine": "^1.1.0",
"karma-junit-reporter": "^1.2.0",
"karma-ng-scenario": "^1.0.0",
"karma-safari-launcher": "^1.0.0",
"karma-sauce-launcher": "^1.2.0",
"karma-script-launcher": "^1.0.0",
"karma-spec-reporter": "^0.0.32",
"karma-spec-reporter": "^0.0.31",
"load-grunt-tasks": "^3.5.0",
"lodash": "~2.4.1",
"log4js": "^0.6.27",
@@ -84,13 +85,13 @@
"npm-run": "^4.1.0",
"open-sans-fontface": "^1.4.0",
"promises-aplus-tests": "~2.1.0",
"protractor": "^5.1.2",
"protractor": "^5.4.1",
"q": "~1.0.0",
"q-io": "^1.10.9",
"qq": "^0.3.5",
"rewire": "~2.1.0",
"sax": "^1.1.1",
"selenium-webdriver": "^2.53.1",
"selenium-webdriver": "^4.0.0-alpha.1",
"semver": "^5.4.1",
"serve-favicon": "^2.3.0",
"serve-index": "^1.8.0",
+1 -1
View File
@@ -12,7 +12,7 @@ set -xe
# This is the default set of browsers to use on the CI server unless overridden via env variable
if [[ -z "$BROWSERS" ]]
then
BROWSERS="Chrome"
BROWSERS="Chrome,Firefox,/Users/jenkins/bin/safari.sh"
fi
# CLEAN #
+2 -7
View File
@@ -28,6 +28,8 @@
"REGEX_STRING_REGEXP" : false,
"lowercase": false,
"uppercase": false,
"manualLowercase": false,
"manualUppercase": false,
"isArrayLike": false,
"forEach": false,
"forEachSorted": false,
@@ -159,7 +161,6 @@
/* urlUtils.js */
"urlResolve": false,
"urlIsSameOrigin": false,
"urlIsSameOriginAsBaseUrl": false,
"urlIsAllowedOriginFactory": false,
/* ng/controller.js */
@@ -171,15 +172,9 @@
/* ng/q.js */
"markQExceptionHandled": false,
/* sce.js */
"SCE_CONTEXTS": false,
/* ng/directive/directives.js */
"ngDirective": false,
/* ng/directive/ngEventDirs.js */
"createEventDirective": false,
/* ng/directive/input.js */
"VALID_CLASS": false,
"INVALID_CLASS": false,
+73 -35
View File
@@ -21,6 +21,8 @@
lowercase,
uppercase,
manualLowercase,
manualUppercase,
nodeName_,
isArrayLike,
forEach,
@@ -128,7 +130,15 @@ var VALIDITY_STATE_PROPERTY = 'validity';
var hasOwnProperty = Object.prototype.hasOwnProperty;
/**
* @private
* @ngdoc function
* @name angular.lowercase
* @module ng
* @kind function
*
* @deprecated
* sinceVersion="1.5.0"
* removeVersion="1.7.0"
* Use [String.prototype.toLowerCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase) instead.
*
* @description Converts the specified string to lowercase.
* @param {string} string String to be converted to lowercase.
@@ -137,7 +147,15 @@ var hasOwnProperty = Object.prototype.hasOwnProperty;
var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
/**
* @private
* @ngdoc function
* @name angular.uppercase
* @module ng
* @kind function
*
* @deprecated
* sinceVersion="1.5.0"
* removeVersion="1.7.0"
* Use [String.prototype.toUpperCase](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) instead.
*
* @description Converts the specified string to uppercase.
* @param {string} string String to be converted to uppercase.
@@ -146,6 +164,31 @@ var lowercase = function(string) {return isString(string) ? string.toLowerCase()
var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
var manualLowercase = function(s) {
/* eslint-disable no-bitwise */
return isString(s)
? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
: s;
/* eslint-enable */
};
var manualUppercase = function(s) {
/* eslint-disable no-bitwise */
return isString(s)
? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
: s;
/* eslint-enable */
};
// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
// with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387
if ('i' !== 'I'.toLowerCase()) {
lowercase = manualLowercase;
uppercase = manualUppercase;
}
var
msie, // holds major version number for IE, or NaN if UA is not IE.
jqLite, // delay binding since jQuery could be loaded after us.
@@ -193,7 +236,8 @@ function isArrayLike(obj) {
// NodeList objects (with `item` method) and
// other objects with suitable length characteristics are array-like
return isNumber(length) && (length >= 0 && (length - 1) in obj || typeof obj.item === 'function');
return isNumber(length) &&
(length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item === 'function');
}
@@ -342,8 +386,10 @@ function baseExtend(dst, objs, deep) {
} else if (isElement(src)) {
dst[key] = src.clone();
} else {
if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
baseExtend(dst[key], [src], true);
if (key !== '__proto__') {
if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
baseExtend(dst[key], [src], true);
}
}
} else {
dst[key] = src;
@@ -608,14 +654,12 @@ function isDate(value) {
* @kind function
*
* @description
* Determines if a reference is an `Array`.
* Determines if a reference is an `Array`. Alias of Array.isArray.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is an `Array`.
*/
function isArray(arr) {
return Array.isArray(arr) || arr instanceof Array;
}
var isArray = Array.isArray;
/**
* @description
@@ -792,16 +836,15 @@ function arrayRemove(array, value) {
* * 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`.
* @param {(Object|Array)=} destination Destination into which the source is copied. If provided,
* must be of the same type as `source`.
* @param {*} source The source that will be used to make a copy.
* Can be any type, including primitives, `null`, and `undefined`.
* @param {(Object|Array)=} destination Destination into which the source is copied. If
* provided, must be of the same type as `source`.
* @returns {*} The copy or updated `destination`, if `destination` was specified.
*
* @example
@@ -1696,13 +1739,8 @@ function angularInit(element, bootstrap) {
});
if (appElement) {
if (!isAutoBootstrapAllowed) {
try {
window.console.error('AngularJS: disabling automatic bootstrap. <script> protocol indicates ' +
window.console.error('AngularJS: disabling automatic bootstrap. <script> protocol indicates ' +
'an extension, document.location.href does not match.');
} catch (e) {
// Support: Safari 11 w/ Webdriver
// The console.error will throw and make the test fail
}
return;
}
config.strictDi = getNgAttribute(appElement, 'strict-di') !== null;
@@ -1904,25 +1942,25 @@ function bindJQuery() {
injector: JQLitePrototype.injector,
inheritedData: JQLitePrototype.inheritedData
});
// All nodes removed from the DOM via various jQuery APIs like .remove()
// are passed through jQuery.cleanData. Monkey-patch this method to fire
// the $destroy event on all removed nodes.
originalCleanData = jQuery.cleanData;
jQuery.cleanData = function(elems) {
var events;
for (var i = 0, elem; (elem = elems[i]) != null; i++) {
events = jQuery._data(elem, 'events');
if (events && events.$destroy) {
jQuery(elem).triggerHandler('$destroy');
}
}
originalCleanData(elems);
};
} else {
jqLite = JQLite;
}
// All nodes removed from the DOM via various jqLite/jQuery APIs like .remove()
// are passed through jqLite/jQuery.cleanData. Monkey-patch this method to fire
// the $destroy event on all removed nodes.
originalCleanData = jqLite.cleanData;
jqLite.cleanData = function(elems) {
var events;
for (var i = 0, elem; (elem = elems[i]) != null; i++) {
events = (jqLite._data(elem) || {}).events;
if (events && events.$destroy) {
jqLite(elem).triggerHandler('$destroy');
}
}
originalCleanData(elems);
};
angular.element = jqLite;
// Prevent double-proxying.
+6 -11
View File
@@ -7,7 +7,7 @@
htmlAnchorDirective,
inputDirective,
inputDirective,
hiddenInputBrowserCacheDirective,
formDirective,
scriptDirective,
selectDirective,
@@ -28,7 +28,6 @@
ngInitDirective,
ngNonBindableDirective,
ngPluralizeDirective,
ngRefDirective,
ngRepeatDirective,
ngShowDirective,
ngStyleDirective,
@@ -70,7 +69,6 @@
$FilterProvider,
$$ForceReflowProvider,
$InterpolateProvider,
$$IntervalFactoryProvider,
$IntervalProvider,
$HttpProvider,
$HttpParamSerializerProvider,
@@ -89,7 +87,6 @@
$SceProvider,
$SceDelegateProvider,
$SnifferProvider,
$$TaskTrackerFactoryProvider,
$TemplateCacheProvider,
$TemplateRequestProvider,
$$TestabilityProvider,
@@ -153,6 +150,8 @@ function publishExternalAPI(angular) {
'isArray': isArray,
'version': version,
'isDate': isDate,
'lowercase': lowercase,
'uppercase': uppercase,
'callbacks': {$$counter: 0},
'getTestability': getTestability,
'reloadWithDebugInfo': reloadWithDebugInfo,
@@ -160,9 +159,7 @@ function publishExternalAPI(angular) {
'$$csp': csp,
'$$encodeUriSegment': encodeUriSegment,
'$$encodeUriQuery': encodeUriQuery,
'$$lowercase': lowercase,
'$$stringify': stringify,
'$$uppercase': uppercase
'$$stringify': stringify
});
angularModule = setupModuleLoader(window);
@@ -197,7 +194,6 @@ function publishExternalAPI(angular) {
ngInit: ngInitDirective,
ngNonBindable: ngNonBindableDirective,
ngPluralize: ngPluralizeDirective,
ngRef: ngRefDirective,
ngRepeat: ngRepeatDirective,
ngShow: ngShowDirective,
ngStyle: ngStyleDirective,
@@ -221,7 +217,8 @@ function publishExternalAPI(angular) {
ngModelOptions: ngModelOptionsDirective
}).
directive({
ngInclude: ngIncludeFillContentDirective
ngInclude: ngIncludeFillContentDirective,
input: hiddenInputBrowserCacheDirective
}).
directive(ngAttributeAliasDirectives).
directive(ngEventDirectives);
@@ -243,7 +240,6 @@ function publishExternalAPI(angular) {
$$forceReflow: $$ForceReflowProvider,
$interpolate: $InterpolateProvider,
$interval: $IntervalProvider,
$$intervalFactory: $$IntervalFactoryProvider,
$http: $HttpProvider,
$httpParamSerializer: $HttpParamSerializerProvider,
$httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
@@ -259,7 +255,6 @@ function publishExternalAPI(angular) {
$sce: $SceProvider,
$sceDelegate: $SceDelegateProvider,
$sniffer: $SnifferProvider,
$$taskTrackerFactory: $$TaskTrackerFactoryProvider,
$templateCache: $TemplateCacheProvider,
$templateRequest: $TemplateRequestProvider,
$$testability: $$TestabilityProvider,
+4 -8
View File
@@ -45,10 +45,11 @@ function NgMapShim() {
}
NgMapShim.prototype = {
_idx: function(key) {
if (key !== this._lastKey) {
this._lastKey = key;
this._lastIndex = this._keys.indexOf(key);
if (key === this._lastKey) {
return this._lastIndex;
}
this._lastKey = key;
this._lastIndex = this._keys.indexOf(key);
return this._lastIndex;
},
_transformKey: function(key) {
@@ -61,11 +62,6 @@ NgMapShim.prototype = {
return this._values[idx];
}
},
has: function(key) {
key = this._transformKey(key);
var idx = this._idx(key);
return idx !== -1;
},
set: function(key, value) {
key = this._transformKey(key);
var idx = this._idx(key);
+3 -1
View File
@@ -922,7 +922,9 @@ function createInjector(modulesToLoad, strictDi) {
}
var result = func.$$ngIsClass;
if (!isBoolean(result)) {
result = func.$$ngIsClass = /^class\b/.test(stringifyFn(func));
// Support: Edge 12-13 only
// See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/
result = func.$$ngIsClass = /^(?:class\b|constructor\()/.test(stringifyFn(func));
}
return result;
}
+9 -28
View File
@@ -311,28 +311,6 @@ function jqLiteDealoc(element, onlyDescendants) {
}
}
function isEmptyObject(obj) {
var name;
for (name in obj) {
return false;
}
return true;
}
function removeIfEmptyData(element) {
var expandoId = element.ng339;
var expandoStore = expandoId && jqCache[expandoId];
var events = expandoStore && expandoStore.events;
var data = expandoStore && expandoStore.data;
if ((!data || isEmptyObject(data)) && (!events || isEmptyObject(events))) {
delete jqCache[expandoId];
element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
}
}
function jqLiteOff(element, type, fn, unsupported) {
if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
@@ -369,8 +347,6 @@ function jqLiteOff(element, type, fn, unsupported) {
}
});
}
removeIfEmptyData(element);
}
function jqLiteRemoveData(element, name) {
@@ -380,11 +356,17 @@ function jqLiteRemoveData(element, name) {
if (expandoStore) {
if (name) {
delete expandoStore.data[name];
} else {
expandoStore.data = {};
return;
}
removeIfEmptyData(element);
if (expandoStore.handle) {
if (expandoStore.events.$destroy) {
expandoStore.handle({}, '$destroy');
}
jqLiteOff(element);
}
delete jqCache[expandoId];
element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
}
}
@@ -634,7 +616,6 @@ forEach({
cleanData: function jqLiteCleanData(nodes) {
for (var i = 0, ii = nodes.length; i < ii; i++) {
jqLiteRemoveData(nodes[i]);
jqLiteOff(nodes[i]);
}
}
}, function(fn, name) {
+1 -2
View File
@@ -328,8 +328,7 @@ function setupModuleLoader(window) {
* @ngdoc method
* @name angular.Module#component
* @module ng
* @param {string|Object} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`),
* or an object map of components where the keys are the names and the values are the component definition objects.
* @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
* @param {Object} options Component definition object (a simplified
* {@link ng.$compile#directive-definition-object directive definition object})
*
+3 -16
View File
@@ -7,8 +7,7 @@
*/
var minErrConfig = {
objectMaxDepth: 5,
urlErrorParamsEnabled: true
objectMaxDepth: 5
};
/**
@@ -31,21 +30,12 @@ var minErrConfig = {
* * `objectMaxDepth` **{Number}** - The max depth for stringifying objects. Setting to a
* non-positive or non-numeric value, removes the max depth limit.
* Default: 5
*
* * `urlErrorParamsEnabled` **{Boolean}** - Specifies wether the generated error url will
* contain the parameters of the thrown error. Disabling the parameters can be useful if the
* generated error url is very long.
*
* Default: true. When used without argument, it returns the current value.
*/
function errorHandlingConfig(config) {
if (isObject(config)) {
if (isDefined(config.objectMaxDepth)) {
minErrConfig.objectMaxDepth = isValidObjectMaxDepth(config.objectMaxDepth) ? config.objectMaxDepth : NaN;
}
if (isDefined(config.urlErrorParamsEnabled) && isBoolean(config.urlErrorParamsEnabled)) {
minErrConfig.urlErrorParamsEnabled = config.urlErrorParamsEnabled;
}
} else {
return minErrConfig;
}
@@ -60,7 +50,6 @@ function isValidObjectMaxDepth(maxDepth) {
return isNumber(maxDepth) && maxDepth > 0;
}
/**
* @description
*
@@ -124,10 +113,8 @@ function minErr(module, ErrorConstructor) {
message += '\n' + url + (module ? module + '/' : '') + code;
if (minErrConfig.urlErrorParamsEnabled) {
for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
}
for (i = 0, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
message += paramPrefix + 'p' + i + '=' + encodeURIComponent(templateArgs[i]);
}
return new ErrorConstructor(message);
+16 -103
View File
@@ -369,39 +369,14 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
* );
* ```
*
* <div class="alert alert-warning">
* **Note**: Generally, the events that are fired correspond 1:1 to `$animate` method names,
* e.g. {@link ng.$animate#addClass addClass()} will fire `addClass`, and {@link ng.ngClass}
* will fire `addClass` if classes are added, and `removeClass` if classes are removed.
* However, there are two exceptions:
*
* <ul>
* <li>if both an {@link ng.$animate#addClass addClass()} and a
* {@link ng.$animate#removeClass removeClass()} action are performed during the same
* animation, the event fired will be `setClass`. This is true even for `ngClass`.</li>
* <li>an {@link ng.$animate#animate animate()} call that adds and removes classes will fire
* the `setClass` event, but if it either removes or adds classes,
* it will fire `animate` instead.</li>
* </ul>
*
* </div>
*
* @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
* @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
* as well as among its children
* @param {Function} callback the callback function that will be fired when the listener is triggered.
* @param {Function} callback the callback function that will be fired when the listener is triggered
*
* The arguments present in the callback function are:
* * `element` - The captured DOM element that the animation was fired on.
* * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
* * `data` - an object with these properties:
* * addClass - `{string|null}` - space-separated CSS classes to add to the element
* * removeClass - `{string|null}` - space-separated CSS classes to remove from the element
* * from - `{Object|null}` - CSS properties & values at the beginning of the animation
* * to - `{Object|null}` - CSS properties & values at the end of the animation
*
* Note that the callback does not trigger a scope digest. Wrap your call into a
* {@link $rootScope.Scope#$apply scope.$apply} to propagate changes to the scope.
*/
on: $$animateQueue.on,
@@ -489,77 +464,13 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
* @ngdoc method
* @name $animate#cancel
* @kind function
* @description Cancels the provided animation and applies the end state of the animation.
* Note that this does not cancel the underlying operation, e.g. the setting of classes or
* adding the element to the DOM.
* @description Cancels the provided animation.
*
* @param {animationRunner} animationRunner An animation runner returned by an $animate function.
*
* @example
<example module="animationExample" deps="angular-animate.js" animations="true" name="animate-cancel">
<file name="app.js">
angular.module('animationExample', ['ngAnimate']).component('cancelExample', {
templateUrl: 'template.html',
controller: function($element, $animate) {
this.runner = null;
this.addClass = function() {
this.runner = $animate.addClass($element.find('div'), 'red');
var ctrl = this;
this.runner.finally(function() {
ctrl.runner = null;
});
};
this.removeClass = function() {
this.runner = $animate.removeClass($element.find('div'), 'red');
var ctrl = this;
this.runner.finally(function() {
ctrl.runner = null;
});
};
this.cancel = function() {
$animate.cancel(this.runner);
};
}
});
</file>
<file name="template.html">
<p>
<button id="add" ng-click="$ctrl.addClass()">Add</button>
<button ng-click="$ctrl.removeClass()">Remove</button>
<br>
<button id="cancel" ng-click="$ctrl.cancel()" ng-disabled="!$ctrl.runner">Cancel</button>
<br>
<div id="target">CSS-Animated Text</div>
</p>
</file>
<file name="index.html">
<cancel-example></cancel-example>
</file>
<file name="style.css">
.red-add, .red-remove {
transition: all 4s cubic-bezier(0.250, 0.460, 0.450, 0.940);
}
.red,
.red-add.red-add-active {
color: #FF0000;
font-size: 40px;
}
.red-remove.red-remove-active {
font-size: 10px;
color: black;
}
</file>
</example>
* @param {Promise} animationPromise The animation promise that is returned when an animation is started.
*/
cancel: function(runner) {
if (runner.cancel) {
runner.cancel();
if (runner.end) {
runner.end();
}
},
@@ -585,7 +496,7 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} the animation runner
* @return {Promise} the animation callback promise
*/
enter: function(element, parent, after, options) {
parent = parent && jqLite(parent);
@@ -617,7 +528,7 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} the animation runner
* @return {Promise} the animation callback promise
*/
move: function(element, parent, after, options) {
parent = parent && jqLite(parent);
@@ -644,7 +555,7 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} the animation runner
* @return {Promise} the animation callback promise
*/
leave: function(element, options) {
return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
@@ -669,11 +580,12 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
* @param {object=} options an optional collection of options/styles that will be applied to the element.
* The object can have the following properties:
*
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **addClass** - `{string}` - space-separated CSS classes to add to element
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} animationRunner the animation runner
* @return {Promise} the animation callback promise
*/
addClass: function(element, className, options) {
options = prepareAnimateOptions(options);
@@ -700,9 +612,10 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
*
* - **addClass** - `{string}` - space-separated CSS classes to add to element
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} the animation runner
* @return {Promise} the animation callback promise
*/
removeClass: function(element, className, options) {
options = prepareAnimateOptions(options);
@@ -729,11 +642,11 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
* The object can have the following properties:
*
* - **addClass** - `{string}` - space-separated CSS classes to add to element
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} the animation runner
* @return {Promise} the animation callback promise
*/
setClass: function(element, add, remove, options) {
options = prepareAnimateOptions(options);
@@ -780,7 +693,7 @@ var $AnimateProvider = ['$provide', /** @this */ function($provide) {
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
*
* @return {Runner} the animation runner
* @return {Promise} the animation callback promise
*/
animate: function(element, from, to, className, options) {
options = prepareAnimateOptions(options);
+65 -47
View File
@@ -1,14 +1,5 @@
'use strict';
/* global getHash: true, stripHash: false */
function getHash(url) {
var index = url.indexOf('#');
return index === -1 ? '' : url.substr(index);
}
function trimEmptyHash(url) {
return url.replace(/#$/, '');
}
/* global stripHash: true */
/**
* ! This is a private undocumented service !
@@ -31,27 +22,61 @@ function trimEmptyHash(url) {
* @param {object} $log window.console or an object with the same interface.
* @param {object} $sniffer $sniffer service
*/
function Browser(window, document, $log, $sniffer, $$taskTrackerFactory) {
function Browser(window, document, $log, $sniffer) {
var self = this,
location = window.location,
history = window.history,
setTimeout = window.setTimeout,
clearTimeout = window.clearTimeout,
pendingDeferIds = {},
taskTracker = $$taskTrackerFactory($log);
pendingDeferIds = {};
self.isMock = false;
//////////////////////////////////////////////////////////////
// Task-tracking API
//////////////////////////////////////////////////////////////
var outstandingRequestCount = 0;
var outstandingRequestCallbacks = [];
// TODO(vojta): remove this temporary api
self.$$completeOutstandingRequest = taskTracker.completeTask;
self.$$incOutstandingRequestCount = taskTracker.incTaskCount;
self.$$completeOutstandingRequest = completeOutstandingRequest;
self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
// TODO(vojta): prefix this method with $$ ?
self.notifyWhenNoOutstandingRequests = taskTracker.notifyWhenNoPendingTasks;
/**
* Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
* counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
*/
function completeOutstandingRequest(fn) {
try {
fn.apply(null, sliceArgs(arguments, 1));
} finally {
outstandingRequestCount--;
if (outstandingRequestCount === 0) {
while (outstandingRequestCallbacks.length) {
try {
outstandingRequestCallbacks.pop()();
} catch (e) {
$log.error(e);
}
}
}
}
}
function getHash(url) {
var index = url.indexOf('#');
return index === -1 ? '' : url.substr(index);
}
/**
* @private
* TODO(vojta): prefix this method with $$ ?
* @param {function()} callback Function that will be called when no outstanding request
*/
self.notifyWhenNoOutstandingRequests = function(callback) {
if (outstandingRequestCount === 0) {
callback();
} else {
outstandingRequestCallbacks.push(callback);
}
};
//////////////////////////////////////////////////////////////
// URL API
@@ -76,21 +101,20 @@ function Browser(window, document, $log, $sniffer, $$taskTrackerFactory) {
*
* @description
* GETTER:
* Without any argument, this method just returns current value of `location.href` (with a
* trailing `#` stripped of if the hash is empty).
* Without any argument, this method just returns current value of location.href.
*
* SETTER:
* With at least one argument, this method sets url to new value.
* If html5 history api supported, `pushState`/`replaceState` is used, otherwise
* `location.href`/`location.replace` is used.
* Returns its own instance to allow chaining.
* If html5 history api supported, pushState/replaceState is used, otherwise
* location.href/location.replace is used.
* Returns its own instance to allow chaining
*
* NOTE: this api is intended for use only by the `$location` service. Please use the
* NOTE: this api is intended for use only by the $location service. Please use the
* {@link ng.$location $location service} to change url.
*
* @param {string} url New url (when used as setter)
* @param {boolean=} replace Should new url replace current history record?
* @param {object=} state State object to use with `pushState`/`replaceState`
* @param {object=} state object to use with pushState/replaceState
*/
self.url = function(url, replace, state) {
// In modern browsers `history.state` is `null` by default; treating it separately
@@ -148,7 +172,8 @@ function Browser(window, document, $log, $sniffer, $$taskTrackerFactory) {
// - pendingLocation is needed as browsers don't allow to read out
// the new location.href if a reload happened or if there is a bug like in iOS 9 (see
// https://openradar.appspot.com/22186109).
return trimEmptyHash(pendingLocation || location.href);
// - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
return pendingLocation || location.href.replace(/%27/g,'\'');
}
};
@@ -283,8 +308,7 @@ function Browser(window, document, $log, $sniffer, $$taskTrackerFactory) {
/**
* @name $browser#defer
* @param {function()} fn A function, who's execution should be deferred.
* @param {number=} [delay=0] Number of milliseconds to defer the function execution.
* @param {string=} [taskType=DEFAULT_TASK_TYPE] The type of task that is deferred.
* @param {number=} [delay=0] of milliseconds to defer the function execution.
* @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
*
* @description
@@ -295,19 +319,14 @@ function Browser(window, document, $log, $sniffer, $$taskTrackerFactory) {
* via `$browser.defer.flush()`.
*
*/
self.defer = function(fn, delay, taskType) {
self.defer = function(fn, delay) {
var timeoutId;
delay = delay || 0;
taskType = taskType || taskTracker.DEFAULT_TASK_TYPE;
taskTracker.incTaskCount(taskType);
outstandingRequestCount++;
timeoutId = setTimeout(function() {
delete pendingDeferIds[timeoutId];
taskTracker.completeTask(fn, taskType);
}, delay);
pendingDeferIds[timeoutId] = taskType;
completeOutstandingRequest(fn);
}, delay || 0);
pendingDeferIds[timeoutId] = true;
return timeoutId;
};
@@ -323,11 +342,10 @@ function Browser(window, document, $log, $sniffer, $$taskTrackerFactory) {
* canceled.
*/
self.defer.cancel = function(deferId) {
if (pendingDeferIds.hasOwnProperty(deferId)) {
var taskType = pendingDeferIds[deferId];
if (pendingDeferIds[deferId]) {
delete pendingDeferIds[deferId];
clearTimeout(deferId);
taskTracker.completeTask(noop, taskType);
completeOutstandingRequest(noop);
return true;
}
return false;
@@ -337,8 +355,8 @@ function Browser(window, document, $log, $sniffer, $$taskTrackerFactory) {
/** @this */
function $BrowserProvider() {
this.$get = ['$window', '$log', '$sniffer', '$document', '$$taskTrackerFactory',
function($window, $log, $sniffer, $document, $$taskTrackerFactory) {
return new Browser($window, $document, $log, $sniffer, $$taskTrackerFactory);
}];
this.$get = ['$window', '$log', '$sniffer', '$document',
function($window, $log, $sniffer, $document) {
return new Browser($window, $document, $log, $sniffer);
}];
}
+178 -657
View File
File diff suppressed because it is too large Load Diff
+22 -3
View File
@@ -26,7 +26,8 @@ function identifierForController(controller, ident) {
* {@link ng.$controllerProvider#register register} method.
*/
function $ControllerProvider() {
var controllers = {};
var controllers = {},
globals = false;
/**
* @ngdoc method
@@ -54,7 +55,22 @@ function $ControllerProvider() {
}
};
this.$get = ['$injector', function($injector) {
/**
* @ngdoc method
* @name $controllerProvider#allowGlobals
* @description If called, allows `$controller` to find controller constructors on `window`
*
* @deprecated
* sinceVersion="v1.3.0"
* removeVersion="v1.7.0"
* This method of finding controllers has been deprecated.
*/
this.allowGlobals = function() {
globals = true;
};
this.$get = ['$injector', '$window', function($injector, $window) {
/**
* @ngdoc service
@@ -67,6 +83,8 @@ function $ControllerProvider() {
*
* * check if a controller with given name is registered via `$controllerProvider`
* * check if evaluating the string on the current scope returns a constructor
* * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
* `window` object (deprecated, not recommended)
*
* The string can use the `controller as property` syntax, where the controller instance is published
* as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
@@ -106,7 +124,8 @@ function $ControllerProvider() {
identifier = identifier || match[3];
expression = controllers.hasOwnProperty(constructor)
? controllers[constructor]
: getter(locals.$scope, constructor, true);
: getter(locals.$scope, constructor, true) ||
(globals ? getter($window, constructor, true) : undefined);
if (!expression) {
throw $controllerMinErr('ctrlreg',
+1 -1
View File
@@ -436,7 +436,7 @@ forEach(['src', 'srcset', 'href'], function(attrName) {
// On IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
// then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
// to set the property as well to achieve the desired effect.
// We use attr[attrName] value since $set might have sanitized the url.
// We use attr[attrName] value since $set can sanitize the url.
if (msie && propName) element.prop(propName, attr[name]);
});
}
+11 -55
View File
@@ -4,14 +4,12 @@
*/
var nullFormCtrl = {
$addControl: noop,
$getControls: valueFn([]),
$$renameControl: nullFormRenameControl,
$removeControl: noop,
$setValidity: noop,
$setDirty: noop,
$setPristine: noop,
$setSubmitted: noop,
$$setSubmitted: noop
$setSubmitted: noop
},
PENDING_CLASS = 'ng-pending',
SUBMITTED_CLASS = 'ng-submitted';
@@ -160,30 +158,6 @@ FormController.prototype = {
control.$$parentForm = this;
},
/**
* @ngdoc method
* @name form.FormController#$getControls
* @returns {Array} the controls that are currently part of this form
*
* @description
* This method returns a **shallow copy** of the controls that are currently part of this form.
* The controls can be instances of {@link form.FormController `FormController`}
* ({@link ngForm "child-forms"}) and of {@link ngModel.NgModelController `NgModelController`}.
* If you need access to the controls of child-forms, you have to call `$getControls()`
* recursively on them.
* This can be used for example to iterate over all controls to validate them.
*
* The controls can be accessed normally, but adding to, or removing controls from the array has
* no effect on the form. Instead, use {@link form.FormController#$addControl `$addControl()`} and
* {@link form.FormController#$removeControl `$removeControl()`} for this use-case.
* Likewise, adding a control to, or removing a control from the form is not reflected
* in the shallow copy. That means you should get a fresh copy from `$getControls()` every time
* you need access to the controls.
*/
$getControls: function() {
return shallowCopy(this.$$controls);
},
// Private API: rename a form control
$$renameControl: function(control, newName) {
var oldName = control.$name;
@@ -300,25 +274,12 @@ FormController.prototype = {
* @name form.FormController#$setSubmitted
*
* @description
* Sets the form to its `$submitted` state. This will also set `$submitted` on all child and
* parent forms of the form.
* Sets the form to its submitted state.
*/
$setSubmitted: function() {
var rootForm = this;
while (rootForm.$$parentForm && (rootForm.$$parentForm !== nullFormCtrl)) {
rootForm = rootForm.$$parentForm;
}
rootForm.$$setSubmitted();
},
$$setSubmitted: function() {
this.$$animate.addClass(this.$$element, SUBMITTED_CLASS);
this.$submitted = true;
forEach(this.$$controls, function(control) {
if (control.$$setSubmitted) {
control.$$setSubmitted();
}
});
this.$$parentForm.$setSubmitted();
}
};
@@ -377,21 +338,16 @@ addSetValidityMethod({
* @restrict EAC
*
* @description
* Helper directive that makes it possible to create control groups inside a
* {@link ng.directive:form `form`} directive.
* These "child forms" can be used, for example, to determine the validity of a sub-group of
* controls.
* Nestable alias of {@link ng.directive:form `form`} directive. HTML
* does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
* sub-group of controls needs to be determined.
*
* <div class="alert alert-danger">
* **Note**: `ngForm` cannot be used as a replacement for `<form>`, because it lacks its
* [built-in HTML functionality](https://html.spec.whatwg.org/#the-form-element).
* Specifically, you cannot submit `ngForm` like a `<form>` tag. That means,
* you cannot send data to the server with `ngForm`, or integrate it with
* {@link ng.directive:ngSubmit `ngSubmit`}.
* </div>
* Note: the purpose of `ngForm` is to group controls,
* but not to be a replacement for the `<form>` tag with all of its capabilities
* (e.g. posting to the server, ...).
*
* @param {string=} ngForm|name Name of the form. If specified, the form controller will
* be published into the related scope, under this name.
* @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
* related scope, under this name.
*
*/
+62 -74
View File
@@ -255,10 +255,6 @@ var inputType = {
* The timezone to be used to read/write the `Date` instance in the model can be defined using
* {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
*
* The format of the displayed time can be adjusted with the
* {@link ng.directive:ngModelOptions#ngModelOptions-arguments ngModelOptions} `timeSecondsFormat`
* and `timeStripZeroSeconds`.
*
* @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
@@ -360,12 +356,7 @@ var inputType = {
* Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
*
* The timezone to be used to read/write the `Date` instance in the model can be defined using
* {@link ng.directive:ngModelOptions#ngModelOptions-arguments ngModelOptions}. By default,
* this is the timezone of the browser.
*
* The format of the displayed time can be adjusted with the
* {@link ng.directive:ngModelOptions#ngModelOptions-arguments ngModelOptions} `timeSecondsFormat`
* and `timeStripZeroSeconds`.
* {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
*
* @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
@@ -466,10 +457,6 @@ var inputType = {
* The model must always be a Date object, otherwise AngularJS will throw an error.
* Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
*
* The value of the resulting Date object will be set to Thursday at 00:00:00 of the requested week,
* due to ISO-8601 week numbering standards. Information on ISO's system for numbering the weeks of the
* year can be found at: https://en.wikipedia.org/wiki/ISO_8601#Week_dates
*
* The timezone to be used to read/write the `Date` instance in the model can be defined using
* {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
*
@@ -679,11 +666,7 @@ var inputType = {
* error docs for more information and an example of how to convert your model if necessary.
* </div>
*
*
*
* @knownIssue
*
* ### HTML5 constraint validation and `allowInvalid`
* ## Issues with HTML5 constraint validation
*
* In browsers that follow the
* [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
@@ -692,17 +675,6 @@ var inputType = {
* which means the view / model values in `ngModel` and subsequently the scope value
* will also be an empty string.
*
* @knownIssue
*
* ### Large numbers and `step` validation
*
* The `step` validation will not work correctly for very large numbers (e.g. 9999999999) due to
* Javascript's arithmetic limitations. If you need to handle large numbers, purpose-built
* libraries (e.g. https://github.com/MikeMcl/big.js/), can be included into AngularJS by
* {@link guide/forms#modifying-built-in-validators overwriting the validators}
* for `number` and / or `step`, or by {@link guide/forms#custom-validation applying custom validators}
* to an `input[text]` element. The source for `input[number]` type can be used as a starting
* point for both implementations.
*
* @param {string} ngModel Assignable AngularJS expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
@@ -1151,8 +1123,8 @@ var inputType = {
* Can be interpolated.
* @param {string=} step Sets the `step` validation to ensure that the value entered matches the `step`
* Can be interpolated.
* @param {expression=} ngChange AngularJS expression to be executed when the ngModel value changes due
* to user interaction with the input element.
* @param {string=} ngChange AngularJS expression to be executed when the ngModel value changes due
* to user interaction with the input element.
* @param {expression=} ngChecked If the expression is truthy, then the `checked` attribute will be set on the
* element. **Note** : `ngChecked` should not be used alongside `ngModel`.
* Checkout {@link ng.directive:ngChecked ngChecked} for usage.
@@ -1498,13 +1470,12 @@ function createDateParser(regexp, mapping) {
function createDateInputType(type, regexp, parseDate, format) {
return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
badInputChecker(scope, element, attr, ctrl, type);
badInputChecker(scope, element, attr, ctrl);
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
var isTimeType = type === 'time' || type === 'datetimelocal';
var previousDate;
var previousTimezone;
ctrl.$$parserName = type;
ctrl.$parsers.push(function(value) {
if (ctrl.$isEmpty(value)) return null;
@@ -1514,7 +1485,6 @@ function createDateInputType(type, regexp, parseDate, format) {
// contains some different data format!
return parseDateAndConvertTimeZoneToLocal(value, previousDate);
}
ctrl.$$parserName = type;
return undefined;
});
@@ -1525,13 +1495,11 @@ function createDateInputType(type, regexp, parseDate, format) {
if (isValidDate(value)) {
previousDate = value;
var timezone = ctrl.$options.getOption('timezone');
if (timezone) {
previousTimezone = timezone;
previousDate = convertTimezoneToLocal(previousDate, timezone, true);
}
return formatter(value, timezone);
return $filter('date')(value, format, timezone);
} else {
previousDate = null;
previousTimezone = null;
@@ -1586,49 +1554,25 @@ function createDateInputType(type, regexp, parseDate, format) {
}
return parsedDate;
}
function formatter(value, timezone) {
var targetFormat = format;
if (isTimeType && isString(ctrl.$options.getOption('timeSecondsFormat'))) {
targetFormat = format
.replace('ss.sss', ctrl.$options.getOption('timeSecondsFormat'))
.replace(/:$/, '');
}
var formatted = $filter('date')(value, targetFormat, timezone);
if (isTimeType && ctrl.$options.getOption('timeStripZeroSeconds')) {
formatted = formatted.replace(/(?::00)?(?:\.000)?$/, '');
}
return formatted;
}
};
}
function badInputChecker(scope, element, attr, ctrl, parserName) {
function badInputChecker(scope, element, attr, ctrl) {
var node = element[0];
var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
if (nativeValidation) {
ctrl.$parsers.push(function(value) {
var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
if (validity.badInput || validity.typeMismatch) {
ctrl.$$parserName = parserName;
return undefined;
}
return value;
return validity.badInput || validity.typeMismatch ? undefined : value;
});
}
}
function numberFormatterParser(ctrl) {
ctrl.$$parserName = 'number';
ctrl.$parsers.push(function(value) {
if (ctrl.$isEmpty(value)) return null;
if (NUMBER_REGEXP.test(value)) return parseFloat(value);
ctrl.$$parserName = 'number';
return undefined;
});
@@ -1710,7 +1654,7 @@ function isValidForStep(viewValue, stepBase, step) {
}
function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
badInputChecker(scope, element, attr, ctrl, 'number');
badInputChecker(scope, element, attr, ctrl);
numberFormatterParser(ctrl);
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
@@ -1718,8 +1662,8 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
var maxVal;
if (isDefined(attr.min) || attr.ngMin) {
ctrl.$validators.min = function(modelValue, viewValue) {
return ctrl.$isEmpty(viewValue) || isUndefined(minVal) || viewValue >= minVal;
ctrl.$validators.min = function(value) {
return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
};
attr.$observe('min', function(val) {
@@ -1730,8 +1674,8 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
}
if (isDefined(attr.max) || attr.ngMax) {
ctrl.$validators.max = function(modelValue, viewValue) {
return ctrl.$isEmpty(viewValue) || isUndefined(maxVal) || viewValue <= maxVal;
ctrl.$validators.max = function(value) {
return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
};
attr.$observe('max', function(val) {
@@ -1757,7 +1701,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
}
function rangeInputType(scope, element, attr, ctrl, $sniffer, $browser) {
badInputChecker(scope, element, attr, ctrl, 'range');
badInputChecker(scope, element, attr, ctrl);
numberFormatterParser(ctrl);
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
@@ -1896,6 +1840,7 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
stringBasedInputType(ctrl);
ctrl.$$parserName = 'url';
ctrl.$validators.url = function(modelValue, viewValue) {
var value = modelValue || viewValue;
return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
@@ -1908,6 +1853,7 @@ function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
stringBasedInputType(ctrl);
ctrl.$$parserName = 'email';
ctrl.$validators.email = function(modelValue, viewValue) {
var value = modelValue || viewValue;
return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
@@ -1932,7 +1878,7 @@ function radioInputType(scope, element, attr, ctrl) {
}
};
element.on('change', listener);
element.on('click', listener);
ctrl.$render = function() {
var value = attr.value;
@@ -1966,7 +1912,7 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
ctrl.$setViewValue(element[0].checked, ev && ev.type);
};
element.on('change', listener);
element.on('click', listener);
ctrl.$render = function() {
element[0].checked = ctrl.$viewValue;
@@ -2193,6 +2139,48 @@ var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
}];
var hiddenInputBrowserCacheDirective = function() {
var valueProperty = {
configurable: true,
enumerable: false,
get: function() {
return this.getAttribute('value') || '';
},
set: function(val) {
this.setAttribute('value', val);
}
};
return {
restrict: 'E',
priority: 200,
compile: function(_, attr) {
if (lowercase(attr.type) !== 'hidden') {
return;
}
return {
pre: function(scope, element, attr, ctrls) {
var node = element[0];
// Support: Edge
// Moving the DOM around prevents autofillling
if (node.parentNode) {
node.parentNode.insertBefore(node, node.nextSibling);
}
// Support: FF, IE
// Avoiding direct assignment to .value prevents autofillling
if (Object.defineProperty) {
Object.defineProperty(node, 'value', valueProperty);
}
}
};
}
};
};
var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
/**
+46 -4
View File
@@ -14,6 +14,13 @@ function classDirective(name, selector) {
return {
restrict: 'AC',
link: function(scope, element, attr) {
var expression = attr[name].trim();
var isOneTime = (expression.charAt(0) === ':') && (expression.charAt(1) === ':');
var watchInterceptor = isOneTime ? toFlatValue : toClassString;
var watchExpression = $parse(expression, watchInterceptor);
var watchAction = isOneTime ? ngClassOneTimeWatchAction : ngClassWatchAction;
var classCounts = element.data('$classCounts');
var oldModulo = true;
var oldClassString;
@@ -36,7 +43,7 @@ function classDirective(name, selector) {
scope.$watch(indexWatchExpression, ngClassIndexWatchAction);
}
scope.$watch($parse(attr[name], toClassString), ngClassWatchAction);
scope.$watch(watchExpression, watchAction, isOneTime);
function addClasses(classString) {
classString = digestClassCounts(split(classString), 1);
@@ -78,9 +85,9 @@ function classDirective(name, selector) {
}
function ngClassIndexWatchAction(newModulo) {
// This watch-action should run before the `ngClassWatchAction()`, thus it
// This watch-action should run before the `ngClass[OneTime]WatchAction()`, thus it
// adds/removes `oldClassString`. If the `ngClass` expression has changed as well, the
// `ngClassWatchAction()` will update the classes.
// `ngClass[OneTime]WatchAction()` will update the classes.
if (newModulo === selector) {
addClasses(oldClassString);
} else {
@@ -90,6 +97,14 @@ function classDirective(name, selector) {
oldModulo = newModulo;
}
function ngClassOneTimeWatchAction(newClassValue) {
var newClassString = toClassString(newClassValue);
if (newClassString !== oldClassString) {
ngClassWatchAction(newClassString);
}
}
function ngClassWatchAction(newClassString) {
if (oldModulo === selector) {
updateClasses(oldClassString, newClassString);
@@ -137,6 +152,34 @@ function classDirective(name, selector) {
return classString;
}
function toFlatValue(classValue) {
var flatValue = classValue;
if (isArray(classValue)) {
flatValue = classValue.map(toFlatValue);
} else if (isObject(classValue)) {
var hasUndefined = false;
flatValue = Object.keys(classValue).filter(function(key) {
var value = classValue[key];
if (!hasUndefined && isUndefined(value)) {
hasUndefined = true;
}
return value;
});
if (hasUndefined) {
// Prevent the `oneTimeLiteralWatchInterceptor` from unregistering
// the watcher, by including at least one `undefined` value.
flatValue.push(undefined);
}
}
return flatValue;
}
}
/**
@@ -178,7 +221,6 @@ function classDirective(name, selector) {
* |----------------------------------|-------------------------------------|
* | {@link ng.$animate#addClass addClass} | just before the class is applied to the element |
* | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
* | {@link ng.$animate#setClass setClass} | just before classes are added and classes are removed from the element at the same time |
*
* ### ngClass and pre-existing CSS3 Transitions/Animations
The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
+4
View File
@@ -31,6 +31,10 @@
* The controller instance can be published into a scope property by specifying
* `ng-controller="as propertyName"`.
*
* If the current `$controllerProvider` is configured to use globals (via
* {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
* also be the name of a globally accessible constructor function (deprecated, not recommended).
*
* @example
* Here is a simple form for editing user contact information. Adding, removing, clearing, and
* greeting are methods declared on the controller (see source tab). These methods can
+23 -34
View File
@@ -50,44 +50,33 @@ forEach(
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
function(eventName) {
var directiveName = directiveNormalize('ng-' + eventName);
ngEventDirectives[directiveName] = ['$parse', '$rootScope', '$exceptionHandler', function($parse, $rootScope, $exceptionHandler) {
return createEventDirective($parse, $rootScope, $exceptionHandler, directiveName, eventName, forceAsyncEvents[eventName]);
ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
return {
restrict: 'A',
compile: function($element, attr) {
// NOTE:
// We expose the powerful `$event` object on the scope that provides access to the Window,
// etc. This is OK, because expressions are not sandboxed any more (and the expression
// sandbox was never meant to be a security feature anyway).
var fn = $parse(attr[directiveName]);
return function ngEventHandler(scope, element) {
element.on(eventName, function(event) {
var callback = function() {
fn(scope, {$event: event});
};
if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
scope.$evalAsync(callback);
} else {
scope.$apply(callback);
}
});
};
}
};
}];
}
);
function createEventDirective($parse, $rootScope, $exceptionHandler, directiveName, eventName, forceAsync) {
return {
restrict: 'A',
compile: function($element, attr) {
// NOTE:
// We expose the powerful `$event` object on the scope that provides access to the Window,
// etc. This is OK, because expressions are not sandboxed any more (and the expression
// sandbox was never meant to be a security feature anyway).
var fn = $parse(attr[directiveName]);
return function ngEventHandler(scope, element) {
element.on(eventName, function(event) {
var callback = function() {
fn(scope, {$event: event});
};
if (!$rootScope.$$phase) {
scope.$apply(callback);
} else if (forceAsync) {
scope.$evalAsync(callback);
} else {
try {
callback();
} catch (error) {
$exceptionHandler(error);
}
}
});
};
}
};
}
/**
* @ngdoc directive
* @name ngDblclick
+6 -16
View File
@@ -282,12 +282,12 @@ function NgModelController($scope, $exceptionHandler, $attr, $element, $parse, $
this.$$ngModelSet = this.$$parsedNgModelAssign;
this.$$pendingDebounce = null;
this.$$parserValid = undefined;
this.$$parserName = 'parse';
this.$$currentValidationRunId = 0;
this.$$scope = $scope;
this.$$rootScope = $scope.$root;
// https://github.com/angular/angular.js/issues/15833
// Prevent `$$scope` from being iterated over by `copy` when NgModelController is deep watched
Object.defineProperty(this, '$$scope', {value: $scope});
this.$$attr = $attr;
this.$$element = $element;
this.$$animate = $animate;
@@ -614,8 +614,7 @@ NgModelController.prototype = {
processAsyncValidators();
function processParseErrors() {
var errorKey = that.$$parserName;
var errorKey = that.$$parserName || 'parse';
if (isUndefined(that.$$parserValid)) {
setValidity(errorKey, null);
} else {
@@ -627,7 +626,6 @@ NgModelController.prototype = {
setValidity(name, null);
});
}
// Set the parse error last, to prevent unsetting it, should a $validators key == parserName
setValidity(errorKey, that.$$parserValid);
return that.$$parserValid;
@@ -730,10 +728,6 @@ NgModelController.prototype = {
this.$$parserValid = isUndefined(modelValue) ? undefined : true;
// Reset any previous parse error
this.$setValidity(this.$$parserName, null);
this.$$parserName = 'parse';
if (this.$$parserValid) {
for (var i = 0; i < this.$parsers.length; i++) {
modelValue = this.$parsers[i](modelValue);
@@ -851,12 +845,8 @@ NgModelController.prototype = {
if (isNumber(debounceDelay[trigger])) {
debounceDelay = debounceDelay[trigger];
} else if (isNumber(debounceDelay['default']) &&
this.$options.getOption('updateOn').indexOf(trigger) === -1
) {
} else if (isNumber(debounceDelay['default'])) {
debounceDelay = debounceDelay['default'];
} else if (isNumber(debounceDelay['*'])) {
debounceDelay = debounceDelay['*'];
}
this.$$timeout.cancel(this.$$pendingDebounce);
@@ -865,7 +855,7 @@ NgModelController.prototype = {
this.$$pendingDebounce = this.$$timeout(function() {
that.$commitViewValue();
}, debounceDelay);
} else if (this.$$rootScope.$$phase) {
} else if (this.$$scope.$root.$$phase) {
this.$commitViewValue();
} else {
this.$$scope.$apply(function() {
+12 -196
View File
@@ -41,7 +41,7 @@ ModelOptions.prototype = {
options = extend({}, options);
// Inherit options from the parent if specified by the value `"$inherit"`
forEach(options, /** @this */ function(option, key) {
forEach(options, /* @this */ function(option, key) {
if (option === '$inherit') {
if (key === '*') {
inheritAll = true;
@@ -264,105 +264,6 @@ defaultModelOptions = new ModelOptions({
* </file>
* </example>
*
* ### Default events, extra triggers, and catch-all debounce values
*
* This example shows the relationship between "default" update events and
* additional `updateOn` triggers.
*
* `default` events are those that are bound to the control, and when fired, update the `$viewValue`
* via {@link ngModel.NgModelController#$setViewValue $setViewValue}. Every event that is not listed
* in `updateOn` is considered a "default" event, since different control types have different
* default events.
*
* The control in this example updates by "default", "click", and "blur", with different `debounce`
* values. You can see that "click" doesn't have an individual `debounce` value -
* therefore it uses the `*` debounce value.
*
* There is also a button that calls {@link ngModel.NgModelController#$setViewValue $setViewValue}
* directly with a "custom" event. Since "custom" is not defined in the `updateOn` list,
* it is considered a "default" event and will update the
* control if "default" is defined in `updateOn`, and will receive the "default" debounce value.
* Note that this is just to illustrate how custom controls would possibly call `$setViewValue`.
*
* You can change the `updateOn` and `debounce` configuration to test different scenarios. This
* is done with {@link ngModel.NgModelController#$overrideModelOptions $overrideModelOptions}.
*
<example name="ngModelOptions-advanced" module="optionsExample">
<file name="index.html">
<model-update-demo></model-update-demo>
</file>
<file name="app.js">
angular.module('optionsExample', [])
.component('modelUpdateDemo', {
templateUrl: 'template.html',
controller: function() {
this.name = 'Chinua';
this.options = {
updateOn: 'default blur click',
debounce: {
default: 2000,
blur: 0,
'*': 1000
}
};
this.updateEvents = function() {
var eventList = this.options.updateOn.split(' ');
eventList.push('*');
var events = {};
for (var i = 0; i < eventList.length; i++) {
events[eventList[i]] = this.options.debounce[eventList[i]];
}
this.events = events;
};
this.updateOptions = function() {
var options = angular.extend(this.options, {
updateOn: Object.keys(this.events).join(' ').replace('*', ''),
debounce: this.events
});
this.form.input.$overrideModelOptions(options);
};
// Initialize the event form
this.updateEvents();
}
});
</file>
<file name="template.html">
<form name="$ctrl.form">
Input: <input type="text" name="input" ng-model="$ctrl.name" ng-model-options="$ctrl.options" />
</form>
Model: <tt>{{$ctrl.name}}</tt>
<hr>
<button ng-click="$ctrl.form.input.$setViewValue('some value', 'custom')">Trigger setViewValue with 'some value' and 'custom' event</button>
<hr>
<form ng-submit="$ctrl.updateOptions()">
<b>updateOn</b><br>
<input type="text" ng-model="$ctrl.options.updateOn" ng-change="$ctrl.updateEvents()" ng-model-options="{debounce: 500}">
<table>
<tr>
<th>Option</th>
<th>Debounce value</th>
</tr>
<tr ng-repeat="(key, value) in $ctrl.events">
<td>{{key}}</td>
<td><input type="number" ng-model="$ctrl.events[key]" /></td>
</tr>
</table>
<br>
<input type="submit" value="Update options">
</form>
</file>
</example>
*
*
* ## Model updates and validation
*
@@ -406,81 +307,22 @@ defaultModelOptions = new ModelOptions({
* </example>
*
*
* ## Programmatically changing options
*
* The `ngModelOptions` expression is only evaluated once when the directive is linked; it is not
* watched for changes. However, it is possible to override the options on a single
* {@link ngModel.NgModelController} instance with
* {@link ngModel.NgModelController#$overrideModelOptions `NgModelController#$overrideModelOptions()`}.
* See also the example for
* {@link ngModelOptions#default-events-extra-triggers-and-catch-all-debounce-values
* Default events, extra triggers, and catch-all debounce values}.
*
*
* ## Specifying timezones
*
* You can specify the timezone that date/time input directives expect by providing its name in the
* `timezone` property.
*
*
* ## Formatting the value of `time` and `datetime-local`
* ## Programmatically changing options
*
* With the options `timeSecondsFormat` and `timeStripZeroSeconds` it is possible to adjust the value
* that is displayed in the control. Note that browsers may apply their own formatting
* in the user interface.
* The `ngModelOptions` expression is only evaluated once when the directive is linked; it is not
* watched for changes. However, it is possible to override the options on a single
* {@link ngModel.NgModelController} instance with
* {@link ngModel.NgModelController#$overrideModelOptions `NgModelController#$overrideModelOptions()`}.
*
<example name="ngModelOptions-time-format" module="timeExample">
<file name="index.html">
<time-example></time-example>
</file>
<file name="script.js">
angular.module('timeExample', [])
.component('timeExample', {
templateUrl: 'timeExample.html',
controller: function() {
this.time = new Date(1970, 0, 1, 14, 57, 0);
this.options = {
timeSecondsFormat: 'ss',
timeStripZeroSeconds: true
};
this.optionChange = function() {
this.timeForm.timeFormatted.$overrideModelOptions(this.options);
this.time = new Date(this.time);
};
}
});
</file>
<file name="timeExample.html">
<form name="$ctrl.timeForm">
<strong>Default</strong>:
<input type="time" ng-model="$ctrl.time" step="any" /><br>
<strong>With options</strong>:
<input type="time" name="timeFormatted" ng-model="$ctrl.time" step="any" ng-model-options="$ctrl.options" />
<br>
Options:<br>
<code>timeSecondsFormat</code>:
<input
type="text"
ng-model="$ctrl.options.timeSecondsFormat"
ng-change="$ctrl.optionChange()">
<br>
<code>timeStripZeroSeconds</code>:
<input
type="checkbox"
ng-model="$ctrl.options.timeStripZeroSeconds"
ng-change="$ctrl.optionChange()">
</form>
</file>
* </example>
*
* @param {Object} ngModelOptions options to apply to {@link ngModel} directives on this element and
* and its descendents.
*
* **General options**:
*
* and its descendents. Valid keys are:
* - `updateOn`: string specifying which event should the input be bound to. You can set several
* events using an space delimited list. There is a special event called `default` that
* matches the default events belonging to the control. These are the events that are bound to
@@ -497,26 +339,18 @@ defaultModelOptions = new ModelOptions({
* custom value for each event. For example:
* ```
* ng-model-options="{
* updateOn: 'default blur',
* updateOn: 'default blur click',
* debounce: { 'default': 500, 'blur': 0 }
* }"
* ```
* You can use the `*` key to specify a debounce value that applies to all events that are not
* specifically listed. In the following example, `mouseup` would have a debounce delay of 1000:
* ```
* ng-model-options="{
* updateOn: 'default blur mouseup',
* debounce: { 'default': 500, 'blur': 0, '*': 1000 }
* }"
* ```
*
* "default" also applies to all events that are listed in `updateOn` but are not
* listed in `debounce`, i.e. "click" would also be debounced by 500 milliseconds.
*
* - `allowInvalid`: boolean value which indicates that the model can be set with values that did
* not validate correctly instead of the default behavior of setting the model to undefined.
* - `getterSetter`: boolean value which determines whether or not to treat functions bound to
* `ngModel` as getters/setters.
*
*
* **Input-type specific options**:
*
* - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
* `<input type="date" />`, `<input type="time" />`, ... . It understands UTC/GMT and the
* continental US time zone abbreviations, but for general use, use a time zone offset, for
@@ -525,24 +359,6 @@ defaultModelOptions = new ModelOptions({
* Note that changing the timezone will have no effect on the current date, and is only applied after
* the next input / model change.
*
* - `timeSecondsFormat`: Defines if the `time` and `datetime-local` types should show seconds and
* milliseconds. The option follows the format string of {@link date date filter}.
* By default, the options is `undefined` which is equal to `'ss.sss'` (seconds and milliseconds).
* The other options are `'ss'` (strips milliseconds), and `''` (empty string), which strips both
* seconds and milliseconds.
* Note that browsers that support `time` and `datetime-local` require the hour and minutes
* part of the time string, and may show the value differently in the user interface.
* {@link ngModelOptions#formatting-the-value-of-time-and-datetime-local- See the example}.
*
* - `timeStripZeroSeconds`: Defines if the `time` and `datetime-local` types should strip the
* seconds and milliseconds from the formatted value if they are zero. This option is applied
* after `timeSecondsFormat`.
* This option can be used to make the formatting consistent over different browsers, as some
* browsers with support for `time` will natively hide the milliseconds and
* seconds if they are zero, but others won't, and browsers that don't implement these input
* types will always show the full string.
* {@link ngModelOptions#formatting-the-value-of-time-and-datetime-local- See the example}.
*
*/
var ngModelOptionsDirective = function() {
NgModelOptionsController.$inject = ['$attrs', '$scope'];
-296
View File
@@ -1,296 +0,0 @@
'use strict';
/**
* @ngdoc directive
* @name ngRef
* @restrict A
*
* @description
* The `ngRef` attribute tells AngularJS to assign the controller of a component (or a directive)
* to the given property in the current scope. It is also possible to add the jqlite-wrapped DOM
* element to the scope.
*
* If the element with `ngRef` is destroyed `null` is assigned to the property.
*
* Note that if you want to assign from a child into the parent scope, you must initialize the
* target property on the parent scope, otherwise `ngRef` will assign on the child scope.
* This commonly happens when assigning elements or components wrapped in {@link ngIf} or
* {@link ngRepeat}. See the second example below.
*
*
* @element ANY
* @param {string} ngRef property name - A valid AngularJS expression identifier to which the
* controller or jqlite-wrapped DOM element will be bound.
* @param {string=} ngRefRead read value - The name of a directive (or component) on this element,
* or the special string `$element`. If a name is provided, `ngRef` will
* assign the matching controller. If `$element` is provided, the element
* itself is assigned (even if a controller is available).
*
*
* @example
* ### Simple toggle
* This example shows how the controller of the component toggle
* is reused in the template through the scope to use its logic.
* <example name="ng-ref-component" module="myApp">
* <file name="index.html">
* <my-toggle ng-ref="myToggle"></my-toggle>
* <button ng-click="myToggle.toggle()">Toggle</button>
* <div ng-show="myToggle.isOpen()">
* You are using a component in the same template to show it.
* </div>
* </file>
* <file name="index.js">
* angular.module('myApp', [])
* .component('myToggle', {
* controller: function ToggleController() {
* var opened = false;
* this.isOpen = function() { return opened; };
* this.toggle = function() { opened = !opened; };
* }
* });
* </file>
* <file name="protractor.js" type="protractor">
* it('should publish the toggle into the scope', function() {
* var toggle = element(by.buttonText('Toggle'));
* expect(toggle.evaluate('myToggle.isOpen()')).toEqual(false);
* toggle.click();
* expect(toggle.evaluate('myToggle.isOpen()')).toEqual(true);
* });
* </file>
* </example>
*
* @example
* ### ngRef inside scopes
* This example shows how `ngRef` works with child scopes. The `ngRepeat`-ed `myWrapper` components
* are assigned to the scope of `myRoot`, because the `toggles` property has been initialized.
* The repeated `myToggle` components are published to the child scopes created by `ngRepeat`.
* `ngIf` behaves similarly - the assignment of `myToggle` happens in the `ngIf` child scope,
* because the target property has not been initialized on the `myRoot` component controller.
*
* <example name="ng-ref-scopes" module="myApp">
* <file name="index.html">
* <my-root></my-root>
* </file>
* <file name="index.js">
* angular.module('myApp', [])
* .component('myRoot', {
* templateUrl: 'root.html',
* controller: function() {
* this.wrappers = []; // initialize the array so that the wrappers are assigned into the parent scope
* }
* })
* .component('myToggle', {
* template: '<strong>myToggle</strong><button ng-click="$ctrl.toggle()" ng-transclude></button>',
* transclude: true,
* controller: function ToggleController() {
* var opened = false;
* this.isOpen = function() { return opened; };
* this.toggle = function() { opened = !opened; };
* }
* })
* .component('myWrapper', {
* transclude: true,
* template: '<strong>myWrapper</strong>' +
* '<div>ngRepeatToggle.isOpen(): {{$ctrl.ngRepeatToggle.isOpen() | json}}</div>' +
* '<my-toggle ng-ref="$ctrl.ngRepeatToggle"><ng-transclude></ng-transclude></my-toggle>'
* });
* </file>
* <file name="root.html">
* <strong>myRoot</strong>
* <my-toggle ng-ref="$ctrl.outerToggle">Outer Toggle</my-toggle>
* <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
* <div><em>wrappers assigned to root</em><br>
* <div ng-repeat="wrapper in $ctrl.wrappers">
* wrapper.ngRepeatToggle.isOpen(): {{wrapper.ngRepeatToggle.isOpen() | json}}
* </div>
*
* <ul>
* <li ng-repeat="(index, value) in [1,2,3]">
* <strong>ngRepeat</strong>
* <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
* <my-wrapper ng-ref="$ctrl.wrappers[index]">ngRepeat Toggle {{$index + 1}}</my-wrapper>
* </li>
* </ul>
*
* <div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen()}} // This is always undefined because it's
* assigned to the child scope created by ngIf.
* </div>
* <div ng-if="true">
<strong>ngIf</strong>
* <my-toggle ng-ref="ngIfToggle">ngIf Toggle</my-toggle>
* <div>ngIfToggle.isOpen(): {{ngIfToggle.isOpen() | json}}</div>
* <div>outerToggle.isOpen(): {{$ctrl.outerToggle.isOpen() | json}}</div>
* </div>
* </file>
* <file name="styles.css">
* ul {
* list-style: none;
* padding-left: 0;
* }
*
* li[ng-repeat] {
* background: lightgreen;
* padding: 8px;
* margin: 8px;
* }
*
* [ng-if] {
* background: lightgrey;
* padding: 8px;
* }
*
* my-root {
* background: lightgoldenrodyellow;
* padding: 8px;
* display: block;
* }
*
* my-wrapper {
* background: lightsalmon;
* padding: 8px;
* display: block;
* }
*
* my-toggle {
* background: lightblue;
* padding: 8px;
* display: block;
* }
* </file>
* <file name="protractor.js" type="protractor">
* var OuterToggle = function() {
* this.toggle = function() {
* element(by.buttonText('Outer Toggle')).click();
* };
* this.isOpen = function() {
* return element.all(by.binding('outerToggle.isOpen()')).first().getText();
* };
* };
* var NgRepeatToggle = function(i) {
* var parent = element.all(by.repeater('(index, value) in [1,2,3]')).get(i - 1);
* this.toggle = function() {
* element(by.buttonText('ngRepeat Toggle ' + i)).click();
* };
* this.isOpen = function() {
* return parent.element(by.binding('ngRepeatToggle.isOpen() | json')).getText();
* };
* this.isOuterOpen = function() {
* return parent.element(by.binding('outerToggle.isOpen() | json')).getText();
* };
* };
* var NgRepeatToggles = function() {
* var toggles = [1,2,3].map(function(i) { return new NgRepeatToggle(i); });
* this.forEach = function(fn) {
* toggles.forEach(fn);
* };
* this.isOuterOpen = function(i) {
* return toggles[i - 1].isOuterOpen();
* };
* };
* var NgIfToggle = function() {
* var parent = element(by.css('[ng-if]'));
* this.toggle = function() {
* element(by.buttonText('ngIf Toggle')).click();
* };
* this.isOpen = function() {
* return by.binding('ngIfToggle.isOpen() | json').getText();
* };
* this.isOuterOpen = function() {
* return parent.element(by.binding('outerToggle.isOpen() | json')).getText();
* };
* };
*
* it('should toggle the outer toggle', function() {
* var outerToggle = new OuterToggle();
* expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): false');
* outerToggle.toggle();
* expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): true');
* });
*
* it('should toggle all outer toggles', function() {
* var outerToggle = new OuterToggle();
* var repeatToggles = new NgRepeatToggles();
* var ifToggle = new NgIfToggle();
* expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): false');
* expect(repeatToggles.isOuterOpen(1)).toEqual('outerToggle.isOpen(): false');
* expect(repeatToggles.isOuterOpen(2)).toEqual('outerToggle.isOpen(): false');
* expect(repeatToggles.isOuterOpen(3)).toEqual('outerToggle.isOpen(): false');
* expect(ifToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): false');
* outerToggle.toggle();
* expect(outerToggle.isOpen()).toEqual('outerToggle.isOpen(): true');
* expect(repeatToggles.isOuterOpen(1)).toEqual('outerToggle.isOpen(): true');
* expect(repeatToggles.isOuterOpen(2)).toEqual('outerToggle.isOpen(): true');
* expect(repeatToggles.isOuterOpen(3)).toEqual('outerToggle.isOpen(): true');
* expect(ifToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): true');
* });
*
* it('should toggle each repeat iteration separately', function() {
* var repeatToggles = new NgRepeatToggles();
*
* repeatToggles.forEach(function(repeatToggle) {
* expect(repeatToggle.isOpen()).toEqual('ngRepeatToggle.isOpen(): false');
* expect(repeatToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): false');
* repeatToggle.toggle();
* expect(repeatToggle.isOpen()).toEqual('ngRepeatToggle.isOpen(): true');
* expect(repeatToggle.isOuterOpen()).toEqual('outerToggle.isOpen(): false');
* });
* });
* </file>
* </example>
*
*/
var ngRefMinErr = minErr('ngRef');
var ngRefDirective = ['$parse', function($parse) {
return {
priority: -1, // Needed for compatibility with element transclusion on the same element
restrict: 'A',
compile: function(tElement, tAttrs) {
// Get the expected controller name, converts <data-some-thing> into "someThing"
var controllerName = directiveNormalize(nodeName_(tElement));
// Get the expression for value binding
var getter = $parse(tAttrs.ngRef);
var setter = getter.assign || function() {
throw ngRefMinErr('nonassign', 'Expression in ngRef="{0}" is non-assignable!', tAttrs.ngRef);
};
return function(scope, element, attrs) {
var refValue;
if (attrs.hasOwnProperty('ngRefRead')) {
if (attrs.ngRefRead === '$element') {
refValue = element;
} else {
refValue = element.data('$' + attrs.ngRefRead + 'Controller');
if (!refValue) {
throw ngRefMinErr(
'noctrl',
'The controller for ngRefRead="{0}" could not be found on ngRef="{1}"',
attrs.ngRefRead,
tAttrs.ngRef
);
}
}
} else {
refValue = element.data('$' + controllerName + 'Controller');
}
refValue = refValue || element;
setter(scope, refValue);
// when the element is removed, remove it (nullify it)
element.on('$destroy', function() {
// only remove it if value has not changed,
// because animations (and other procedures) may duplicate elements
if (getter(scope) === refValue) {
setter(scope, null);
}
});
};
}
};
}];
+1 -1
View File
@@ -74,7 +74,7 @@
* For example, if an item is added to the collection, `ngRepeat` will know that all other items
* already have DOM elements, and will not re-render them.
*
* All different types of tracking functions, their syntax, and their support for duplicate
* All different types of tracking functions, their syntax, and and their support for duplicate
* items in collections can be found in the
* {@link ngRepeat#ngRepeat-arguments ngRepeat expression description}.
*
+2 -2
View File
@@ -52,10 +52,10 @@
</example>
*/
var ngStyleDirective = ngDirective(function(scope, element, attr) {
scope.$watchCollection(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
if (oldStyles && (newStyles !== oldStyles)) {
forEach(oldStyles, function(val, style) { element.css(style, '');});
}
if (newStyles) element.css(newStyles);
});
}, true);
});
+13 -1
View File
@@ -383,7 +383,7 @@ var SelectController =
if (optionAttrs.$attr.ngValue) {
// The value attribute is set by ngValue
var oldVal, hashedVal;
var oldVal, hashedVal = NaN;
optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
var removal;
@@ -556,6 +556,18 @@ var SelectController =
* {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
*
*
* @knownIssue
*
* In Firefox, the select model is only updated when the select element is blurred. For example,
* when switching between options with the keyboard, the select model is only set to the
* currently selected option when the select is blurred, e.g via tab key or clicking the mouse
* outside the select.
*
* This is due to an ambiguity in the select element specification. See the
* [issue on the Firefox bug tracker](https://bugzilla.mozilla.org/show_bug.cgi?id=126379)
* for more information, and this
* [Github comment for a workaround](https://github.com/angular/angular.js/issues/9134#issuecomment-130800488)
*
* @example
* ### Simple `select` elements with static options
*
+4 -4
View File
@@ -61,7 +61,7 @@
});
* </file>
* </example>
*/
*/ /** CLOSURE HACK COMMENT */
var requiredDirective = function() {
return {
restrict: 'A',
@@ -161,7 +161,7 @@ var requiredDirective = function() {
});
* </file>
* </example>
*/
*/ /** CLOSURE HACK COMMENT */
var patternDirective = function() {
return {
restrict: 'A',
@@ -263,7 +263,7 @@ var patternDirective = function() {
});
* </file>
* </example>
*/
*/ /** CLOSURE HACK COMMENT */
var maxlengthDirective = function() {
return {
restrict: 'A',
@@ -352,7 +352,7 @@ var maxlengthDirective = function() {
});
* </file>
* </example>
*/
*/ /** CLOSURE HACK COMMENT */
var minlengthDirective = function() {
return {
restrict: 'A',
+8 -17
View File
@@ -40,7 +40,6 @@
* index: ...
* }
* ```
* **Note:** `null` values use `'null'` as their type.
* 2. The comparator function is used to sort the items, based on the derived values, types and
* indices.
*
@@ -75,15 +74,11 @@
*
* The default, built-in comparator should be sufficient for most usecases. In short, it compares
* numbers numerically, strings alphabetically (and case-insensitively), for objects falls back to
* using their index in the original collection, sorts values of different types by type and puts
* `undefined` and `null` values at the end of the sorted list.
* using their index in the original collection, and sorts values of different types by type.
*
* More specifically, it follows these steps to determine the relative order of items:
*
* 1. If the compared values are of different types:
* - If one of the values is undefined, consider it "greater than" the other.
* - Else if one of the values is null, consider it "greater than" the other.
* - Else compare the types themselves alphabetically.
* 1. If the compared values are of different types, compare the types themselves alphabetically.
* 2. If both values are of type `string`, compare them alphabetically in a case- and
* locale-insensitive way.
* 3. If both values are objects, compare their indices instead.
@@ -94,10 +89,9 @@
*
* **Note:** If you notice numbers not being sorted as expected, make sure they are actually being
* saved as numbers and not strings.
* **Note:** For the purpose of sorting, `null` and `undefined` are considered "greater than"
* any other value (with undefined "greater than" null). This effectively means that `null`
* and `undefined` values end up at the end of a list sorted in ascending order.
* **Note:** `null` values use `'null'` as their type to be able to distinguish them from objects.
* **Note:** For the purpose of sorting, `null` values are treated as the string `'null'` (i.e.
* `type: 'string'`, `value: 'null'`). This may cause unexpected sort order relative to
* other values.
*
* @param {Array|ArrayLike} collection - The collection (array or array-like object) to sort.
* @param {(Function|string|Array.<Function|string>)=} expression - A predicate (or list of
@@ -664,7 +658,8 @@ function orderByFilter($parse) {
function getPredicateValue(value, index) {
var type = typeof value;
if (value === null) {
type = 'null';
type = 'string';
value = 'null';
} else if (type === 'object') {
value = objectValue(value);
}
@@ -695,11 +690,7 @@ function orderByFilter($parse) {
result = value1 < value2 ? -1 : 1;
}
} else {
result = (type1 === 'undefined') ? 1 :
(type2 === 'undefined') ? -1 :
(type1 === 'null') ? 1 :
(type2 === 'null') ? -1 :
(type1 < type2) ? -1 : 1;
result = type1 < type2 ? -1 : 1;
}
return result;
+4 -7
View File
@@ -110,6 +110,7 @@ function $HttpParamSerializerJQLikeProvider() {
return parts.join('&');
function serialize(toSerialize, prefix, topLevel) {
if (toSerialize === null || isUndefined(toSerialize)) return;
if (isArray(toSerialize)) {
forEach(toSerialize, function(value, index) {
serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
@@ -122,11 +123,7 @@ function $HttpParamSerializerJQLikeProvider() {
(topLevel ? '' : ']'));
});
} else {
if (isFunction(toSerialize)) {
toSerialize = toSerialize();
}
parts.push(encodeUriQuery(prefix) + '=' +
(toSerialize == null ? '' : encodeUriQuery(serializeValue(toSerialize))));
parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
}
}
};
@@ -1054,7 +1051,7 @@ function $HttpProvider() {
config.paramSerializer = isString(config.paramSerializer) ?
$injector.get(config.paramSerializer) : config.paramSerializer;
$browser.$$incOutstandingRequestCount('$http');
$browser.$$incOutstandingRequestCount();
var requestInterceptors = [];
var responseInterceptors = [];
@@ -1092,7 +1089,7 @@ function $HttpProvider() {
}
function completeOutstandingRequest() {
$browser.$$completeOutstandingRequest(noop, '$http');
$browser.$$completeOutstandingRequest(noop);
}
function executeHeaderFns(headers, config) {
+30 -53
View File
@@ -73,8 +73,9 @@ function $InterpolateProvider() {
if (value) {
startSymbol = value;
return this;
} else {
return startSymbol;
}
return startSymbol;
};
/**
@@ -90,8 +91,9 @@ function $InterpolateProvider() {
if (value) {
endSymbol = value;
return this;
} else {
return endSymbol;
}
return endSymbol;
};
@@ -138,7 +140,7 @@ function $InterpolateProvider() {
* ```js
* var $interpolate = ...; // injected
* var exp = $interpolate('Hello {{name | uppercase}}!');
* expect(exp({name:'AngularJS'})).toEqual('Hello ANGULARJS!');
* expect(exp({name:'AngularJS'})).toEqual('Hello ANGULAR!');
* ```
*
* `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
@@ -238,21 +240,16 @@ function $InterpolateProvider() {
* - `context`: evaluation context for all expressions embedded in the interpolated text
*/
function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
var contextAllowsConcatenation = trustedContext === $sce.URL || trustedContext === $sce.MEDIA_URL;
// Provide a quick exit and simplified result function for text with no interpolation
if (!text.length || text.indexOf(startSymbol) === -1) {
if (mustHaveExpression && !contextAllowsConcatenation) return;
var unescapedText = unescapeText(text);
if (contextAllowsConcatenation) {
unescapedText = $sce.getTrusted(trustedContext, unescapedText);
var constantInterp;
if (!mustHaveExpression) {
var unescapedText = unescapeText(text);
constantInterp = valueFn(unescapedText);
constantInterp.exp = text;
constantInterp.expressions = [];
constantInterp.$$watchDelegate = constantWatchDelegate;
}
var constantInterp = valueFn(unescapedText);
constantInterp.exp = text;
constantInterp.expressions = [];
constantInterp.$$watchDelegate = constantWatchDelegate;
return constantInterp;
}
@@ -261,13 +258,11 @@ function $InterpolateProvider() {
endIndex,
index = 0,
expressions = [],
parseFns,
parseFns = [],
textLength = text.length,
exp,
concat = [],
expressionPositions = [],
singleExpression;
expressionPositions = [];
while (index < textLength) {
if (((startIndex = text.indexOf(startSymbol, index)) !== -1) &&
@@ -277,9 +272,10 @@ function $InterpolateProvider() {
}
exp = text.substring(startIndex + startSymbolLength, endIndex);
expressions.push(exp);
parseFns.push($parse(exp, parseStringifyInterceptor));
index = endIndex + endSymbolLength;
expressionPositions.push(concat.length);
concat.push(''); // Placeholder that will get replaced with the evaluated expression.
concat.push('');
} else {
// we did not find an interpolation, so we have to add the remainder to the separators array
if (index !== textLength) {
@@ -289,25 +285,15 @@ function $InterpolateProvider() {
}
}
singleExpression = concat.length === 1 && expressionPositions.length === 1;
// Intercept expression if we need to stringify concatenated inputs, which may be SCE trusted
// objects rather than simple strings
// (we don't modify the expression if the input consists of only a single trusted input)
var interceptor = contextAllowsConcatenation && singleExpression ? undefined : parseStringifyInterceptor;
parseFns = expressions.map(function(exp) { return $parse(exp, interceptor); });
// Concatenating expressions makes it hard to reason about whether some combination of
// concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
// single expression be used for some $sce-managed secure contexts (RESOURCE_URLs mostly),
// we ensure that the value that's used is assigned or constructed by some JS code somewhere
// that is more testable or make it obvious that you bound the value to some user controlled
// value. This helps reduce the load when auditing for XSS issues.
// Note that URL and MEDIA_URL $sce contexts do not need this, since `$sce` can sanitize the values
// passed to it. In that case, `$sce.getTrusted` will be called on either the single expression
// or on the overall concatenated string (losing trusted types used in the mix, by design).
// Both these methods will sanitize plain strings. Also, HTML could be included, but since it's
// only used in srcdoc attributes, this would not be very useful.
// single expression be used for iframe[src], object[src], etc., we ensure that the value
// that's used is assigned or constructed by some JS code somewhere that is more testable or
// make it obvious that you bound the value to some user controlled value. This helps reduce
// the load when auditing for XSS issues.
if (trustedContext && concat.length > 1) {
$interpolateMinErr.throwNoconcat(text);
}
if (!mustHaveExpression || expressions.length) {
var compute = function(values) {
@@ -315,18 +301,15 @@ function $InterpolateProvider() {
if (allOrNothing && isUndefined(values[i])) return;
concat[expressionPositions[i]] = values[i];
}
if (contextAllowsConcatenation) {
// If `singleExpression` then `concat[0]` might be a "trusted" value or `null`, rather than a string
return $sce.getTrusted(trustedContext, singleExpression ? concat[0] : concat.join(''));
} else if (trustedContext && concat.length > 1) {
// This context does not allow more than one part, e.g. expr + string or exp + exp.
$interpolateMinErr.throwNoconcat(text);
}
// In an unprivileged context or only one part: just concatenate and return.
return concat.join('');
};
var getValue = function(value) {
return trustedContext ?
$sce.getTrusted(trustedContext, value) :
$sce.valueOf(value);
};
return extend(function interpolationFn(context) {
var i = 0;
var ii = expressions.length;
@@ -359,13 +342,7 @@ function $InterpolateProvider() {
function parseStringifyInterceptor(value) {
try {
// In concatenable contexts, getTrusted comes at the end, to avoid sanitizing individual
// parts of a full URL. We don't care about losing the trustedness here.
// In non-concatenable contexts, where there is only one expression, this interceptor is
// not applied to the expression.
value = (trustedContext && !contextAllowsConcatenation) ?
$sce.getTrusted(trustedContext, value) :
$sce.valueOf(value);
value = getValue(value);
return allOrNothing && !isDefined(value) ? value : stringify(value);
} catch (err) {
$exceptionHandler($interpolateMinErr.interr(text, err));
+54 -31
View File
@@ -1,21 +1,11 @@
'use strict';
var $intervalMinErr = minErr('$interval');
/** @this */
function $IntervalProvider() {
this.$get = ['$$intervalFactory', '$window',
function($$intervalFactory, $window) {
this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
function($rootScope, $window, $q, $$q, $browser) {
var intervals = {};
var setIntervalFn = function(tick, delay, deferred) {
var id = $window.setInterval(tick, delay);
intervals[id] = deferred;
return id;
};
var clearIntervalFn = function(id) {
$window.clearInterval(id);
delete intervals[id];
};
/**
* @ngdoc service
@@ -143,7 +133,49 @@ function $IntervalProvider() {
* </file>
* </example>
*/
var interval = $$intervalFactory(setIntervalFn, clearIntervalFn);
function interval(fn, delay, count, invokeApply) {
var hasParams = arguments.length > 4,
args = hasParams ? sliceArgs(arguments, 4) : [],
setInterval = $window.setInterval,
clearInterval = $window.clearInterval,
iteration = 0,
skipApply = (isDefined(invokeApply) && !invokeApply),
deferred = (skipApply ? $$q : $q).defer(),
promise = deferred.promise;
count = isDefined(count) ? count : 0;
promise.$$intervalId = setInterval(function tick() {
if (skipApply) {
$browser.defer(callback);
} else {
$rootScope.$evalAsync(callback);
}
deferred.notify(iteration++);
if (count > 0 && iteration >= count) {
deferred.resolve(iteration);
clearInterval(promise.$$intervalId);
delete intervals[promise.$$intervalId];
}
if (!skipApply) $rootScope.$apply();
}, delay);
intervals[promise.$$intervalId] = deferred;
return promise;
function callback() {
if (!hasParams) {
fn(iteration);
} else {
fn.apply(null, args);
}
}
}
/**
* @ngdoc method
@@ -156,24 +188,15 @@ function $IntervalProvider() {
* @returns {boolean} Returns `true` if the task was successfully canceled.
*/
interval.cancel = function(promise) {
if (!promise) return false;
if (!promise.hasOwnProperty('$$intervalId')) {
throw $intervalMinErr('badprom',
'`$interval.cancel()` called with a promise that was not generated by `$interval()`.');
if (promise && promise.$$intervalId in intervals) {
// Interval cancels should not report as unhandled promise.
markQExceptionHandled(intervals[promise.$$intervalId].promise);
intervals[promise.$$intervalId].reject('canceled');
$window.clearInterval(promise.$$intervalId);
delete intervals[promise.$$intervalId];
return true;
}
if (!intervals.hasOwnProperty(promise.$$intervalId)) return false;
var id = promise.$$intervalId;
var deferred = intervals[id];
// Interval cancels should not report an unhandled promise.
markQExceptionHandled(deferred.promise);
deferred.reject('canceled');
clearIntervalFn(id);
return true;
return false;
};
return interval;
-48
View File
@@ -1,48 +0,0 @@
'use strict';
/** @this */
function $$IntervalFactoryProvider() {
this.$get = ['$browser', '$q', '$$q', '$rootScope',
function($browser, $q, $$q, $rootScope) {
return function intervalFactory(setIntervalFn, clearIntervalFn) {
return function intervalFn(fn, delay, count, invokeApply) {
var hasParams = arguments.length > 4,
args = hasParams ? sliceArgs(arguments, 4) : [],
iteration = 0,
skipApply = isDefined(invokeApply) && !invokeApply,
deferred = (skipApply ? $$q : $q).defer(),
promise = deferred.promise;
count = isDefined(count) ? count : 0;
function callback() {
if (!hasParams) {
fn(iteration);
} else {
fn.apply(null, args);
}
}
function tick() {
if (skipApply) {
$browser.defer(callback);
} else {
$rootScope.$evalAsync(callback);
}
deferred.notify(iteration++);
if (count > 0 && iteration >= count) {
deferred.resolve(iteration);
clearIntervalFn(promise.$$intervalId);
}
if (!skipApply) $rootScope.$apply();
}
promise.$$intervalId = setIntervalFn(tick, delay, deferred, skipApply);
return promise;
};
};
}];
}
+46 -36
View File
@@ -1,5 +1,4 @@
'use strict';
/* global stripHash: true */
var PATH_MATCH = /^([^?#]*)(\?([^#]*))?(#(.*))?$/,
DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
@@ -39,14 +38,6 @@ function decodePath(path, html5Mode) {
return segments.join('/');
}
function normalizePath(pathValue, searchValue, hashValue) {
var search = toKeyValue(searchValue),
hash = hashValue ? '#' + encodeUriSegment(hashValue) : '',
path = encodePath(pathValue);
return path + (search ? '?' + search : '') + hash;
}
function parseAbsoluteUrl(absoluteUrl, locationObj) {
var parsedUrl = urlResolve(absoluteUrl);
@@ -95,11 +86,17 @@ function stripBaseUrl(base, url) {
}
}
function stripHash(url) {
var index = url.indexOf('#');
return index === -1 ? url : url.substr(0, index);
}
function trimEmptyHash(url) {
return url.replace(/(#.+)|#$/, '$1');
}
function stripFile(url) {
return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
}
@@ -146,8 +143,18 @@ function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
this.$$compose();
};
this.$$normalizeUrl = function(url) {
return appBaseNoFile + url.substr(1); // first char is always '/'
/**
* Compose url and update `absUrl` property
* @private
*/
this.$$compose = function() {
var search = toKeyValue(this.$$search),
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
this.$$urlUpdatedByLocation = true;
};
this.$$parseLinkUrl = function(url, relHref) {
@@ -271,8 +278,18 @@ function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
}
};
this.$$normalizeUrl = function(url) {
return appBase + (url ? hashPrefix + url : '');
/**
* Compose hashbang URL and update `absUrl` property
* @private
*/
this.$$compose = function() {
var search = toKeyValue(this.$$search),
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
this.$$urlUpdatedByLocation = true;
};
this.$$parseLinkUrl = function(url, relHref) {
@@ -323,10 +340,17 @@ function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
return !!rewrittenUrl;
};
this.$$normalizeUrl = function(url) {
this.$$compose = function() {
var search = toKeyValue(this.$$search),
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
// include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
return appBase + hashPrefix + url;
this.$$absUrl = appBase + hashPrefix + this.$$url;
this.$$urlUpdatedByLocation = true;
};
}
@@ -350,16 +374,6 @@ var locationPrototype = {
*/
$$replace: false,
/**
* Compose url and update `url` and `absUrl` property
* @private
*/
$$compose: function() {
this.$$url = normalizePath(this.$$path, this.$$search, this.$$hash);
this.$$absUrl = this.$$normalizeUrl(this.$$url);
this.$$urlUpdatedByLocation = true;
},
/**
* @ngdoc method
* @name $location#absUrl
@@ -865,13 +879,6 @@ function $LocationProvider() {
var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
// Determine if two URLs are equal despite potentially having different encoding/normalizing
// such as $location.absUrl() vs $browser.url()
// See https://github.com/angular/angular.js/issues/16592
function urlsEqual(a, b) {
return a === b || urlResolve(a).href === urlResolve(b).href;
}
function setBrowserUrlWithFallback(url, replace, state) {
var oldUrl = $location.url();
var oldState = $location.$$state;
@@ -931,6 +938,8 @@ function $LocationProvider() {
// update location manually
if ($location.absUrl() !== $browser.url()) {
$rootScope.$apply();
// hack to work around FF6 bug 684208 when scenario runner clicks on links
$window.angular['ff-684208-preventDefault'] = true;
}
}
}
@@ -938,7 +947,7 @@ function $LocationProvider() {
// rewrite hashbang url <> html5 url
if ($location.absUrl() !== initialUrl) {
if (trimEmptyHash($location.absUrl()) !== trimEmptyHash(initialUrl)) {
$browser.url($location.absUrl(), true);
}
@@ -957,6 +966,7 @@ function $LocationProvider() {
var oldUrl = $location.absUrl();
var oldState = $location.$$state;
var defaultPrevented;
newUrl = trimEmptyHash(newUrl);
$location.$$parse(newUrl);
$location.$$state = newState;
@@ -984,11 +994,11 @@ function $LocationProvider() {
if (initializing || $location.$$urlUpdatedByLocation) {
$location.$$urlUpdatedByLocation = false;
var oldUrl = $browser.url();
var newUrl = $location.absUrl();
var oldUrl = trimEmptyHash($browser.url());
var newUrl = trimEmptyHash($location.absUrl());
var oldState = $browser.state();
var currentReplace = $location.$$replace;
var urlOrStateChanged = !urlsEqual(oldUrl, newUrl) ||
var urlOrStateChanged = oldUrl !== newUrl ||
($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
if (initializing || urlOrStateChanged) {
+78 -87
View File
@@ -1803,8 +1803,15 @@ function $ParseProvider() {
var lexer = new Lexer($parseOptions);
var parser = new Parser(lexer, $filter, $parseOptions);
parsedExpression = parser.parse(exp);
cache[cacheKey] = addWatchDelegate(parsedExpression);
if (parsedExpression.constant) {
parsedExpression.$$watchDelegate = constantWatchDelegate;
} else if (parsedExpression.oneTime) {
parsedExpression.$$watchDelegate = parsedExpression.literal ?
oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
} else if (parsedExpression.inputs) {
parsedExpression.$$watchDelegate = inputsWatchDelegate;
}
cache[cacheKey] = parsedExpression;
}
return addInterceptor(parsedExpression, interceptorFn);
@@ -1892,48 +1899,57 @@ function $ParseProvider() {
}
function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
var isDone = parsedExpression.literal ? isAllDefined : isDefined;
var unwatch, lastValue;
var exp = parsedExpression.$$intercepted || parsedExpression;
var post = parsedExpression.$$interceptor || identity;
var useInputs = parsedExpression.inputs && !exp.inputs;
// Propogate the literal/inputs/constant attributes
// ... but not oneTime since we are handling it
oneTimeWatch.literal = parsedExpression.literal;
oneTimeWatch.constant = parsedExpression.constant;
oneTimeWatch.inputs = parsedExpression.inputs;
// Allow other delegates to run on this wrapped expression
addWatchDelegate(oneTimeWatch);
unwatch = scope.$watch(oneTimeWatch, listener, objectEquality, prettyPrintExpression);
if (parsedExpression.inputs) {
unwatch = inputsWatchDelegate(scope, oneTimeListener, objectEquality, parsedExpression, prettyPrintExpression);
} else {
unwatch = scope.$watch(oneTimeWatch, oneTimeListener, objectEquality);
}
return unwatch;
function unwatchIfDone() {
if (isDone(lastValue)) {
unwatch();
}
function oneTimeWatch(scope) {
return parsedExpression(scope);
}
function oneTimeWatch(scope, locals, assign, inputs) {
lastValue = useInputs && inputs ? inputs[0] : exp(scope, locals, assign, inputs);
if (isDone(lastValue)) {
scope.$$postDigest(unwatchIfDone);
function oneTimeListener(value, old, scope) {
lastValue = value;
if (isFunction(listener)) {
listener(value, old, scope);
}
if (isDefined(value)) {
scope.$$postDigest(function() {
if (isDefined(lastValue)) {
unwatch();
}
});
}
return post(lastValue);
}
}
function isAllDefined(value) {
var allDefined = true;
forEach(value, function(val) {
if (!isDefined(val)) allDefined = false;
});
return allDefined;
function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
var unwatch, lastValue;
unwatch = scope.$watch(function oneTimeWatch(scope) {
return parsedExpression(scope);
}, function oneTimeListener(value, old, scope) {
lastValue = value;
if (isFunction(listener)) {
listener(value, old, scope);
}
if (isAllDefined(value)) {
scope.$$postDigest(function() {
if (isAllDefined(lastValue)) unwatch();
});
}
}, objectEquality);
return unwatch;
function isAllDefined(value) {
var allDefined = true;
forEach(value, function(val) {
if (!isDefined(val)) allDefined = false;
});
return allDefined;
}
}
function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
@@ -1944,74 +1960,49 @@ function $ParseProvider() {
return unwatch;
}
function addWatchDelegate(parsedExpression) {
if (parsedExpression.constant) {
parsedExpression.$$watchDelegate = constantWatchDelegate;
} else if (parsedExpression.oneTime) {
parsedExpression.$$watchDelegate = oneTimeWatchDelegate;
} else if (parsedExpression.inputs) {
parsedExpression.$$watchDelegate = inputsWatchDelegate;
}
return parsedExpression;
}
function chainInterceptors(first, second) {
function chainedInterceptor(value) {
return second(first(value));
}
chainedInterceptor.$stateful = first.$stateful || second.$stateful;
chainedInterceptor.$$pure = first.$$pure && second.$$pure;
return chainedInterceptor;
}
function addInterceptor(parsedExpression, interceptorFn) {
if (!interceptorFn) return parsedExpression;
// Extract any existing interceptors out of the parsedExpression
// to ensure the original parsedExpression is always the $$intercepted
if (parsedExpression.$$interceptor) {
interceptorFn = chainInterceptors(parsedExpression.$$interceptor, interceptorFn);
parsedExpression = parsedExpression.$$intercepted;
}
var watchDelegate = parsedExpression.$$watchDelegate;
var useInputs = false;
var fn = function interceptedExpression(scope, locals, assign, inputs) {
var regularWatch =
watchDelegate !== oneTimeLiteralWatchDelegate &&
watchDelegate !== oneTimeWatchDelegate;
var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
return interceptorFn(value);
return interceptorFn(value, scope, locals);
} : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
var value = parsedExpression(scope, locals, assign, inputs);
var result = interceptorFn(value, scope, locals);
// we only return the interceptor's result if the
// initial value is defined (for bind-once)
return isDefined(value) ? result : value;
};
// Maintain references to the interceptor/intercepted
fn.$$intercepted = parsedExpression;
fn.$$interceptor = interceptorFn;
// Propogate the literal/oneTime/constant attributes
fn.literal = parsedExpression.literal;
fn.oneTime = parsedExpression.oneTime;
fn.constant = parsedExpression.constant;
// Treat the interceptor like filters.
// If it is not $stateful then only watch its inputs.
// If the expression itself has no inputs then use the full expression as an input.
if (!interceptorFn.$stateful) {
useInputs = !parsedExpression.inputs;
// Propagate $$watchDelegates other then inputsWatchDelegate
useInputs = !parsedExpression.inputs;
if (watchDelegate && watchDelegate !== inputsWatchDelegate) {
fn.$$watchDelegate = watchDelegate;
fn.inputs = parsedExpression.inputs;
} else if (!interceptorFn.$stateful) {
// Treat interceptor like filters - assume non-stateful by default and use the inputsWatchDelegate
fn.$$watchDelegate = inputsWatchDelegate;
fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
}
if (!interceptorFn.$$pure) {
fn.inputs = fn.inputs.map(function(e) {
if (fn.inputs) {
fn.inputs = fn.inputs.map(function(e) {
// Remove the isPure flag of inputs when it is not absolute because they are now wrapped in a
// non-pure interceptor function.
// potentially non-pure interceptor function.
if (e.isPure === PURITY_RELATIVE) {
return function depurifier(s) { return e(s); };
}
return e;
});
}
}
return addWatchDelegate(fn);
return fn;
}
}];
}
+47 -23
View File
@@ -299,8 +299,8 @@ function $RootScopeProvider() {
* according to the {@link angular.equals} function. To save the value of the object for
* later comparison, the {@link angular.copy} function is used. This therefore means that
* watching complex objects will have adverse memory and performance implications.
* - This should not be used to watch for changes in objects that are (or contain)
* [File](https://developer.mozilla.org/docs/Web/API/File) objects due to limitations with {@link angular.copy `angular.copy`}.
* - This should not be used to watch for changes in objects that are
* or contain [File](https://developer.mozilla.org/docs/Web/API/File) objects due to limitations with {@link angular.copy `angular.copy`}.
* - The watch `listener` may change the model, which may trigger other `listener`s to fire.
* This is achieved by rerunning the watchers until no changes are detected. The rerun
* iteration limit is 10 to prevent an infinite loop deadlock.
@@ -448,6 +448,12 @@ function $RootScopeProvider() {
* values are examined for changes on every call to `$digest`.
* - The `listener` is called whenever any expression in the `watchExpressions` array changes.
*
* `$watchGroup` is more performant than watching each expression individually, and should be
* used when the listener does not need to know which expression has changed.
* If the listener needs to know which expression has changed,
* {@link ng.$rootScope.Scope#$watch $watch()} or
* {@link ng.$rootScope.Scope#$watchCollection $watchCollection()} should be used.
*
* @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
* watched using {@link ng.$rootScope.Scope#$watch $watch()}
*
@@ -456,7 +462,34 @@ function $RootScopeProvider() {
* The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
* those of `watchExpression`
* and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
* those of `watchExpression`
* those of `watchExpression`.
*
* Note that `newValues` and `oldValues` reflect the differences in each **individual**
* expression, and not the difference of the values between each call of the listener.
* That means the difference between `newValues` and `oldValues` cannot be used to determine
* which expression has changed / remained stable:
*
* ```js
*
* $scope.$watchGroup(['v1', 'v2'], function(newValues, oldValues) {
* console.log(newValues, oldValues);
* });
*
* // newValues, oldValues initially
* // [undefined, undefined], [undefined, undefined]
*
* $scope.v1 = 'a';
* $scope.v2 = 'a';
*
* // ['a', 'a'], [undefined, undefined]
*
* $scope.v2 = 'b'
*
* // v1 hasn't changed since it became `'a'`, therefore its oldValue is still `undefined`
* // ['a', 'b'], [undefined, 'a']
*
* ```
*
* The `scope` refers to the current scope.
* @returns {function()} Returns a de-registration function for all listeners.
*/
@@ -489,8 +522,9 @@ function $RootScopeProvider() {
}
forEach(watchExpressions, function(expr, i) {
var unwatchFn = self.$watch(expr, function watchGroupSubAction(value) {
var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
newValues[i] = value;
oldValues[i] = oldValue;
if (!changeReactionScheduled) {
changeReactionScheduled = true;
self.$evalAsync(watchGroupAction);
@@ -502,17 +536,11 @@ function $RootScopeProvider() {
function watchGroupAction() {
changeReactionScheduled = false;
try {
if (firstRun) {
firstRun = false;
listener(newValues, newValues, self);
} else {
listener(newValues, oldValues, self);
}
} finally {
for (var i = 0; i < watchExpressions.length; i++) {
oldValues[i] = newValues[i];
}
if (firstRun) {
firstRun = false;
listener(newValues, newValues, self);
} else {
listener(newValues, oldValues, self);
}
}
@@ -580,11 +608,7 @@ function $RootScopeProvider() {
* de-registration function is executed, the internal watch operation is terminated.
*/
$watchCollection: function(obj, listener) {
// Mark the interceptor as
// ... $$pure when literal since the instance will change when any input changes
$watchCollectionInterceptor.$$pure = $parse(obj).literal;
// ... $stateful when non-literal since we must read the state of the collection
$watchCollectionInterceptor.$stateful = !$watchCollectionInterceptor.$$pure;
$watchCollectionInterceptor.$stateful = true;
var self = this;
// the current value, updated on each dirty-check run
@@ -772,7 +796,7 @@ function $RootScopeProvider() {
var watch, value, last, fn, get,
watchers,
dirty, ttl = TTL,
next, current, target = asyncQueue.length ? $rootScope : this,
next, current, target = this,
watchLog = [],
logIdx, asyncTask;
@@ -1122,7 +1146,7 @@ function $RootScopeProvider() {
if (asyncQueue.length) {
$rootScope.$digest();
}
}, null, '$evalAsync');
});
}
asyncQueue.push({scope: this, fn: $parse(expr), locals: locals});
@@ -1493,7 +1517,7 @@ function $RootScopeProvider() {
if (applyAsyncId === null) {
applyAsyncId = $browser.defer(function() {
$rootScope.$apply(flushApplyAsync);
}, null, '$applyAsync');
});
}
}
}];
+14 -24
View File
@@ -6,7 +6,6 @@
* Private service to sanitize uris for links and images. Used by $compile and $sanitize.
*/
function $$SanitizeUriProvider() {
var aHrefSanitizationWhitelist = /^\s*(https?|s?ftp|mailto|tel|file):/,
imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
@@ -15,16 +14,12 @@ function $$SanitizeUriProvider() {
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
* urls during a[href] sanitization.
*
* The sanitization is a security measure aimed at prevent XSS attacks via HTML anchor links.
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
*
* Any url due to be assigned to an `a[href]` attribute via interpolation is marked as requiring
* the $sce.URL security context. When interpolation occurs a call is made to `$sce.trustAsUrl(url)`
* which in turn may call `$$sanitizeUri(url, isMedia)` to sanitize the potentially malicious URL.
*
* If the URL matches the `aHrefSanitizationWhitelist` regular expression, it is returned unchanged.
*
* If there is no match the URL is returned prefixed with `'unsafe:'` to ensure that when it is written
* to the DOM it is inactive and potentially malicious code will not be executed.
* Any url about to be assigned to a[href] via data-binding is first normalized and turned into
* an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
* regular expression. If a match is found, the original url is written into the dom. Otherwise,
* the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
*
* @param {RegExp=} regexp New regexp to whitelist urls with.
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
@@ -44,17 +39,12 @@ function $$SanitizeUriProvider() {
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
* urls during img[src] sanitization.
*
* The sanitization is a security measure aimed at prevent XSS attacks via HTML image src links.
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
*
* Any URL due to be assigned to an `img[src]` attribute via interpolation is marked as requiring
* the $sce.MEDIA_URL security context. When interpolation occurs a call is made to
* `$sce.trustAsMediaUrl(url)` which in turn may call `$$sanitizeUri(url, isMedia)` to sanitize
* the potentially malicious URL.
*
* If the URL matches the `aImgSanitizationWhitelist` regular expression, it is returned unchanged.
*
* If there is no match the URL is returned prefixed with `'unsafe:'` to ensure that when it is written
* to the DOM it is inactive and potentially malicious code will not be executed.
* Any url about to be assigned to img[src] via data-binding is first normalized and turned into
* an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
* regular expression. If a match is found, the original url is written into the dom. Otherwise,
* the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
*
* @param {RegExp=} regexp New regexp to whitelist urls with.
* @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
@@ -69,10 +59,10 @@ function $$SanitizeUriProvider() {
};
this.$get = function() {
return function sanitizeUri(uri, isMediaUrl) {
// if (!uri) return uri;
var regex = isMediaUrl ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
var normalizedVal = urlResolve(uri && uri.trim()).href;
return function sanitizeUri(uri, isImage) {
var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
var normalizedVal;
normalizedVal = urlResolve(uri && uri.trim()).href;
if (normalizedVal !== '' && !normalizedVal.match(regex)) {
return 'unsafe:' + normalizedVal;
}
+32 -67
View File
@@ -22,17 +22,12 @@ var SCE_CONTEXTS = {
// Style statements or stylesheets. Currently unused in AngularJS.
CSS: 'css',
// An URL used in a context where it refers to the source of media, which are not expected to be run
// as scripts, such as an image, audio, video, etc.
MEDIA_URL: 'mediaUrl',
// An URL used in a context where it does not refer to a resource that loads code.
// A value that can be trusted as a URL can also trusted as a MEDIA_URL.
// An URL used in a context where it does not refer to a resource that loads code. Currently
// unused in AngularJS.
URL: 'url',
// RESOURCE_URL is a subtype of URL used where the referred-to resource could be interpreted as
// code. (e.g. ng-include, script src binding, templateUrl)
// A value that can be trusted as a RESOURCE_URL, can also trusted as a URL and a MEDIA_URL.
RESOURCE_URL: 'resourceUrl',
// Script. Currently unused in AngularJS.
@@ -247,7 +242,7 @@ function $SceDelegateProvider() {
return resourceUrlBlacklist;
};
this.$get = ['$injector', '$$sanitizeUri', function($injector, $$sanitizeUri) {
this.$get = ['$injector', function($injector) {
var htmlSanitizer = function htmlSanitizer(html) {
throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
@@ -260,7 +255,7 @@ function $SceDelegateProvider() {
function matchUrl(matcher, parsedUrl) {
if (matcher === 'self') {
return urlIsSameOrigin(parsedUrl) || urlIsSameOriginAsBaseUrl(parsedUrl);
return urlIsSameOrigin(parsedUrl);
} else {
// definitely a regex. See adjustMatchers()
return !!matcher.exec(parsedUrl.href);
@@ -312,8 +307,7 @@ function $SceDelegateProvider() {
byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
byType[SCE_CONTEXTS.MEDIA_URL] = generateHolderType(trustedValueHolderBase);
byType[SCE_CONTEXTS.URL] = generateHolderType(byType[SCE_CONTEXTS.MEDIA_URL]);
byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
@@ -392,27 +386,15 @@ function $SceDelegateProvider() {
* @name $sceDelegate#getTrusted
*
* @description
* Given an object and a security context in which to assign it, returns a value that's safe to
* use in this context, which was represented by the parameter. To do so, this function either
* unwraps the safe type it has been given (for instance, a {@link ng.$sceDelegate#trustAs
* `$sceDelegate.trustAs`} result), or it might try to sanitize the value given, depending on
* the context and sanitizer availablility.
* Takes any input, and either returns a value that's safe to use in the specified context, or
* throws an exception.
*
* The contexts that can be sanitized are $sce.MEDIA_URL, $sce.URL and $sce.HTML. The first two are available
* by default, and the third one relies on the `$sanitize` service (which may be loaded through
* the `ngSanitize` module). Furthermore, for $sce.RESOURCE_URL context, a plain string may be
* accepted if the resource url policy defined by {@link ng.$sceDelegateProvider#resourceUrlWhitelist
* `$sceDelegateProvider.resourceUrlWhitelist`} and {@link ng.$sceDelegateProvider#resourceUrlBlacklist
* `$sceDelegateProvider.resourceUrlBlacklist`} accepts that resource.
*
* This function will throw if the safe type isn't appropriate for this context, or if the
* value given cannot be accepted in the context (which might be caused by sanitization not
* being available, or the value not being recognized as safe).
*
* <div class="alert alert-danger">
* Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
* (XSS) vulnerability in your application.
* </div>
* In practice, there are several cases. When given a string, this function runs checks
* and sanitization to make it safe without prior assumptions. When given the result of a {@link
* ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call, it returns the originally supplied
* value if that value's context is valid for this call's context. Finally, this function can
* also throw when there is no way to turn `maybeTrusted` in a safe value (e.g., no sanitization
* is available or possible.)
*
* @param {string} type The context in which this value is to be used (such as `$sce.HTML`).
* @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
@@ -430,18 +412,12 @@ function $SceDelegateProvider() {
if (constructor && maybeTrusted instanceof constructor) {
return maybeTrusted.$$unwrapTrustedValue();
}
// If maybeTrusted is a trusted class instance but not of the correct trusted type
// then unwrap it and allow it to pass through to the rest of the checks
if (isFunction(maybeTrusted.$$unwrapTrustedValue)) {
maybeTrusted = maybeTrusted.$$unwrapTrustedValue();
}
// If we get here, then we will either sanitize the value or throw an exception.
if (type === SCE_CONTEXTS.MEDIA_URL || type === SCE_CONTEXTS.URL) {
// we attempt to sanitize non-resource URLs
return $$sanitizeUri(maybeTrusted.toString(), type === SCE_CONTEXTS.MEDIA_URL);
} else if (type === SCE_CONTEXTS.RESOURCE_URL) {
// Otherwise, if we get here, then we may either make it safe, or throw an exception. This
// depends on the context: some are sanitizatible (HTML), some use whitelists (RESOURCE_URL),
// some are impossible to do (JS). This step isn't implemented for CSS and URL, as AngularJS
// has no corresponding sinks.
if (type === SCE_CONTEXTS.RESOURCE_URL) {
// RESOURCE_URL uses a whitelist.
if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
return maybeTrusted;
} else {
@@ -596,10 +572,9 @@ function $SceDelegateProvider() {
*
* If your expressions are constant literals, they're automatically trusted and you don't need to
* call `$sce.trustAs` on them (e.g.
* `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works (remember to include the
* `ngSanitize` module). The `$sceDelegate` will also use the `$sanitize` service if it is available
* when binding untrusted values to `$sce.HTML` context.
* AngularJS provides an implementation in `angular-sanitize.js`, and if you
* `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works. The `$sceDelegate` will
* also use the `$sanitize` service if it is available when binding untrusted values to
* `$sce.HTML` context. AngularJS provides an implementation in `angular-sanitize.js`, and if you
* wish to use it, you will also need to depend on the {@link ngSanitize `ngSanitize`} module in
* your application.
*
@@ -619,27 +594,17 @@ function $SceDelegateProvider() {
*
* | Context | Notes |
* |---------------------|----------------|
* | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. |
* | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
* | `$sce.MEDIA_URL` | For URLs that are safe to render as media. Is automatically converted from string by sanitizing when needed. |
* | `$sce.URL` | For URLs that are safe to follow as links. Is automatically converted from string by sanitizing when needed. Note that `$sce.URL` makes a stronger statement about the URL than `$sce.MEDIA_URL` does and therefore contexts requiring values trusted for `$sce.URL` can be used anywhere that values trusted for `$sce.MEDIA_URL` are required.|
* | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` or `$sce.MEDIA_URL` do and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` or `$sce.MEDIA_URL` are required. <br><br> The {@link $sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider#resourceUrlWhitelist()} and {@link $sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider#resourceUrlBlacklist()} can be used to restrict trusted origins for `RESOURCE_URL` |
* | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. |
* | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered, and the {@link ngSanitize.$sanitize $sanitize} service is available (implemented by the {@link ngSanitize ngSanitize} module) this will sanitize the value instead of throwing an error. |
* | `$sce.CSS` | For CSS that's safe to source into the application. Currently, no bindings require this context. Feel free to use it in your own directives. |
* | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=`, `<img src=`, and some others sanitize their urls and don't constitute an SCE context.) |
* | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG`, `VIDEO`, `AUDIO`, `SOURCE`, and `TRACK` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does (it's not just the URL that matters, but also what is at the end of it), and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
* | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently, no bindings require this context. Feel free to use it in your own directives. |
*
*
* <div class="alert alert-warning">
* Be aware that, before AngularJS 1.7.0, `a[href]` and `img[src]` used to sanitize their
* interpolated values directly rather than rely upon {@link ng.$sce#getTrusted `$sce.getTrusted`}.
*
* **As of 1.7.0, this is no longer the case.**
*
* Now such interpolations are marked as requiring `$sce.URL` (for `a[href]`) or `$sce.MEDIA_URL`
* (for `img[src]`), so that the sanitization happens (via `$sce.getTrusted...`) when the `$interpolate`
* service evaluates the expressions.
* </div>
*
* There are no CSS or JS context bindings in AngularJS currently, so their corresponding `$sce.trustAs`
* functions aren't useful yet. This might evolve.
* Be aware that `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
* through {@link ng.$sce#getTrusted $sce.getTrusted}. There's no CSS-, URL-, or JS-context bindings
* in AngularJS currently, so their corresponding `$sce.trustAs` functions aren't useful yet. This
* might evolve.
*
* ### Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
*
@@ -813,7 +778,7 @@ function $SceProvider() {
* such a value.
*
* - getTrusted(contextEnum, value)
* This function should return the value that is safe to use in the context specified by
* This function should return the a value that is safe to use in the context specified by
* contextEnum or throw and exception otherwise.
*
* NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
-122
View File
@@ -1,122 +0,0 @@
'use strict';
/**
* ! This is a private undocumented service !
*
* @name $$taskTrackerFactory
* @description
* A function to create `TaskTracker` instances.
*
* A `TaskTracker` can keep track of pending tasks (grouped by type) and can notify interested
* parties when all pending tasks (or tasks of a specific type) have been completed.
*
* @param {$log} log - A logger instance (such as `$log`). Used to log error during callback
* execution.
*
* @this
*/
function $$TaskTrackerFactoryProvider() {
this.$get = valueFn(function(log) { return new TaskTracker(log); });
}
function TaskTracker(log) {
var self = this;
var taskCounts = {};
var taskCallbacks = [];
var ALL_TASKS_TYPE = self.ALL_TASKS_TYPE = '$$all$$';
var DEFAULT_TASK_TYPE = self.DEFAULT_TASK_TYPE = '$$default$$';
/**
* Execute the specified function and decrement the appropriate `taskCounts` counter.
* If the counter reaches 0, all corresponding `taskCallbacks` are executed.
*
* @param {Function} fn - The function to execute.
* @param {string=} [taskType=DEFAULT_TASK_TYPE] - The type of task that is being completed.
*/
self.completeTask = completeTask;
/**
* Increase the task count for the specified task type (or the default task type if non is
* specified).
*
* @param {string=} [taskType=DEFAULT_TASK_TYPE] - The type of task whose count will be increased.
*/
self.incTaskCount = incTaskCount;
/**
* Execute the specified callback when all pending tasks have been completed.
*
* If there are no pending tasks, the callback is executed immediately. You can optionally limit
* the tasks that will be waited for to a specific type, by passing a `taskType`.
*
* @param {function} callback - The function to call when there are no pending tasks.
* @param {string=} [taskType=ALL_TASKS_TYPE] - The type of tasks that will be waited for.
*/
self.notifyWhenNoPendingTasks = notifyWhenNoPendingTasks;
function completeTask(fn, taskType) {
taskType = taskType || DEFAULT_TASK_TYPE;
try {
fn();
} finally {
decTaskCount(taskType);
var countForType = taskCounts[taskType];
var countForAll = taskCounts[ALL_TASKS_TYPE];
// If at least one of the queues (`ALL_TASKS_TYPE` or `taskType`) is empty, run callbacks.
if (!countForAll || !countForType) {
var getNextCallback = !countForAll ? getLastCallback : getLastCallbackForType;
var nextCb;
while ((nextCb = getNextCallback(taskType))) {
try {
nextCb();
} catch (e) {
log.error(e);
}
}
}
}
}
function decTaskCount(taskType) {
taskType = taskType || DEFAULT_TASK_TYPE;
if (taskCounts[taskType]) {
taskCounts[taskType]--;
taskCounts[ALL_TASKS_TYPE]--;
}
}
function getLastCallback() {
var cbInfo = taskCallbacks.pop();
return cbInfo && cbInfo.cb;
}
function getLastCallbackForType(taskType) {
for (var i = taskCallbacks.length - 1; i >= 0; --i) {
var cbInfo = taskCallbacks[i];
if (cbInfo.type === taskType) {
taskCallbacks.splice(i, 1);
return cbInfo.cb;
}
}
}
function incTaskCount(taskType) {
taskType = taskType || DEFAULT_TASK_TYPE;
taskCounts[taskType] = (taskCounts[taskType] || 0) + 1;
taskCounts[ALL_TASKS_TYPE] = (taskCounts[ALL_TASKS_TYPE] || 0) + 1;
}
function notifyWhenNoPendingTasks(callback, taskType) {
taskType = taskType || ALL_TASKS_TYPE;
if (!taskCounts[taskType]) {
callback();
} else {
taskCallbacks.push({type: taskType, cb: callback});
}
}
}
+3 -2
View File
@@ -1,6 +1,6 @@
'use strict';
var $templateRequestMinErr = minErr('$templateRequest');
var $templateRequestMinErr = minErr('$compile');
/**
* @ngdoc provider
@@ -99,7 +99,8 @@ function $TemplateRequestProvider() {
handleRequestFn.totalPendingRequests--;
})
.then(function(response) {
return $templateCache.put(tpl, response.data);
$templateCache.put(tpl, response.data);
return response.data;
}, handleError);
function handleError(resp) {
+1 -9
View File
@@ -104,15 +104,7 @@ function $$TestabilityProvider() {
* @name $$testability#whenStable
*
* @description
* Calls the callback when all pending tasks are completed.
*
* Types of tasks waited for include:
* - Pending timeouts (via {@link $timeout}).
* - Pending HTTP requests (via {@link $http}).
* - In-progress route transitions (via {@link $route}).
* - Pending tasks scheduled via {@link $rootScope#$applyAsync}.
* - Pending tasks scheduled via {@link $rootScope#$evalAsync}.
* These include tasks scheduled via `$evalAsync()` indirectly (such as {@link $q} promises).
* Calls the callback when $timeout and $http requests are completed.
*
* @param {function} callback
*/
+8 -20
View File
@@ -1,7 +1,5 @@
'use strict';
var $timeoutMinErr = minErr('$timeout');
/** @this */
function $TimeoutProvider() {
this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
@@ -63,7 +61,7 @@ function $TimeoutProvider() {
}
if (!skipApply) $rootScope.$apply();
}, delay, '$timeout');
}, delay);
promise.$$timeoutId = timeoutId;
deferreds[timeoutId] = deferred;
@@ -85,24 +83,14 @@ function $TimeoutProvider() {
* canceled.
*/
timeout.cancel = function(promise) {
if (!promise) return false;
if (!promise.hasOwnProperty('$$timeoutId')) {
throw $timeoutMinErr('badprom',
'`$timeout.cancel()` called with a promise that was not generated by `$timeout()`.');
if (promise && promise.$$timeoutId in deferreds) {
// Timeout cancels should not report an unhandled promise.
markQExceptionHandled(deferreds[promise.$$timeoutId].promise);
deferreds[promise.$$timeoutId].reject('canceled');
delete deferreds[promise.$$timeoutId];
return $browser.defer.cancel(promise.$$timeoutId);
}
if (!deferreds.hasOwnProperty(promise.$$timeoutId)) return false;
var id = promise.$$timeoutId;
var deferred = deferreds[id];
// Timeout cancels should not report an unhandled promise.
markQExceptionHandled(deferred.promise);
deferred.reject('canceled');
delete deferreds[id];
return $browser.defer.cancel(id);
return false;
};
return timeout;
+8 -43
View File
@@ -8,7 +8,6 @@
// service.
var urlParsingNode = window.document.createElement('a');
var originUrl = urlResolve(window.location.href);
var baseUrlParsingNode;
/**
@@ -45,16 +44,16 @@ var baseUrlParsingNode;
* @description Normalizes and parses a URL.
* @returns {object} Returns the normalized URL as a dictionary.
*
* | member name | Description |
* |---------------|------------------------------------------------------------------------|
* | member name | Description |
* |---------------|----------------|
* | href | A normalized version of the provided URL if it was not an absolute URL |
* | protocol | The protocol without the trailing colon |
* | protocol | The protocol including the trailing colon |
* | host | The host and port (if the port is non-default) of the normalizedUrl |
* | search | The search params, minus the question mark |
* | hash | The hash string, minus the hash symbol |
* | hostname | The hostname |
* | port | The port, without ":" |
* | pathname | The pathname, beginning with "/" |
* | hash | The hash string, minus the hash symbol
* | hostname | The hostname
* | port | The port, without ":"
* | pathname | The pathname, beginning with "/"
*
*/
function urlResolve(url) {
@@ -72,6 +71,7 @@ function urlResolve(url) {
urlParsingNode.setAttribute('href', href);
// urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
return {
href: urlParsingNode.href,
protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
@@ -98,20 +98,6 @@ function urlIsSameOrigin(requestUrl) {
return urlsAreSameOrigin(requestUrl, originUrl);
}
/**
* Parse a request URL and determine whether it is same-origin as the current document base URL.
*
* Note: The base URL is usually the same as the document location (`location.href`) but can
* be overriden by using the `<base>` tag.
*
* @param {string|object} requestUrl The url of the request as a string that will be resolved
* or a parsed URL object.
* @returns {boolean} Whether the URL is same-origin as the document base URL.
*/
function urlIsSameOriginAsBaseUrl(requestUrl) {
return urlsAreSameOrigin(requestUrl, getBaseUrl());
}
/**
* Create a function that can check a URL's origin against a list of allowed/whitelisted origins.
* The current location's origin is implicitly trusted.
@@ -157,24 +143,3 @@ function urlsAreSameOrigin(url1, url2) {
return (url1.protocol === url2.protocol &&
url1.host === url2.host);
}
/**
* Returns the current document base URL.
* @returns {string}
*/
function getBaseUrl() {
if (window.document.baseURI) {
return window.document.baseURI;
}
// `document.baseURI` is available everywhere except IE
if (!baseUrlParsingNode) {
baseUrlParsingNode = window.document.createElement('a');
baseUrlParsingNode.href = '.';
// Work-around for IE bug described in Implementation Notes. The fix in `urlResolve()` is not
// suitable here because we need to track changes to the base URL.
baseUrlParsingNode = baseUrlParsingNode.cloneNode(false);
}
return baseUrlParsingNode.href;
}
-1
View File
@@ -73,7 +73,6 @@
/* ngAnimate directives/services */
"ngAnimateSwapDirective": true,
"$$rAFSchedulerFactory": true,
"$$AnimateCacheProvider": true,
"$$AnimateChildrenDirective": true,
"$$AnimateQueueProvider": true,
"$$AnimationProvider": true,

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