Compare commits
354 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d764b581d | |||
| 57a37fcc20 | |||
| 7f6ba5534f | |||
| b24bfae585 | |||
| d3e123b0a6 | |||
| bf1acf7b21 | |||
| fdaf4d5e27 | |||
| 1c47abc462 | |||
| 5222703444 | |||
| 4b38b44c91 | |||
| aa28e48e17 | |||
| f31586db41 | |||
| 8337b9b2d4 | |||
| 85a53ea9cd | |||
| 2d87ef8f23 | |||
| 3df8b637f3 | |||
| 28d3126fe5 | |||
| ad21f8feaf | |||
| 7f2df141cd | |||
| 4e735e5363 | |||
| a9db6073c9 | |||
| e48e27aa2b | |||
| 832b383cbc | |||
| c4bff290e5 | |||
| 55b00148dd | |||
| 23550b5e27 | |||
| 97d2a08c5a | |||
| 7f6efb2822 | |||
| b50867001b | |||
| 8b9ce885d3 | |||
| 56dae6fa1b | |||
| 611dcbc035 | |||
| 2c9066e012 | |||
| 8ecc9357ef | |||
| 71dca7c4c2 | |||
| ce77c25b06 | |||
| a1188721e1 | |||
| 1917ff86c4 | |||
| c60e1960c6 | |||
| 268e71eb8b | |||
| 03e6fb3df5 | |||
| 4fc9cf6289 | |||
| 8dee8f1b9e | |||
| 1eef631ab5 | |||
| 2ef92c329b | |||
| 1467e15bca | |||
| 389349edc3 | |||
| 722e97e8e6 | |||
| d3933a4181 | |||
| 8d02b07af4 | |||
| 24af9e2a6e | |||
| 4879e49c93 | |||
| aec25f1829 | |||
| f87e8288fb | |||
| 9b1beb8e09 | |||
| 681e6246e3 | |||
| 13e2ea73b0 | |||
| f5295ea448 | |||
| bb01b8bf89 | |||
| 711aba7727 | |||
| 88322c1af8 | |||
| 18f055eea5 | |||
| 24a7f28f1e | |||
| 146f9c1611 | |||
| e55829a1cd | |||
| 571e323f7d | |||
| bf11cf3095 | |||
| 5a1e148da6 | |||
| eec095a751 | |||
| 7b2c7cbab8 | |||
| b830f5b68e | |||
| 7e7a0693e5 | |||
| 02929f82f3 | |||
| b9d3625e92 | |||
| 863a4232a6 | |||
| 4a39ad475b | |||
| 696d65dcba | |||
| 947cb4d145 | |||
| 77fc41f499 | |||
| d98a12a6f2 | |||
| ef2c6e39be | |||
| c219a87ad7 | |||
| fad4dc07d7 | |||
| 373f054d5e | |||
| beb00e44de | |||
| 6a4403a118 | |||
| 77cdc37c65 | |||
| ab95ba65c0 | |||
| 86416bcbee | |||
| d04c38c489 | |||
| bdc5a1cde1 | |||
| eccd618d76 | |||
| 7f11af6b5e | |||
| 437ba49a52 | |||
| 9a576fa0fa | |||
| b5c317d672 | |||
| 05741ebf0b | |||
| 9e2c215779 | |||
| 8a1f600c29 | |||
| 8519b8a60f | |||
| 4bc3031497 | |||
| ab5c7698bb | |||
| 89690502d1 | |||
| f47e218006 | |||
| 8dc4c75ade | |||
| e91811095b | |||
| afb298b7ff | |||
| 571afd6558 | |||
| df6e731506 | |||
| af9c2d71e2 | |||
| dc158e7e40 | |||
| 0b7fff303f | |||
| 96d62cc0fc | |||
| 5cb7d0e046 | |||
| a60bbc12e8 | |||
| ca23d5f68f | |||
| a398773b0c | |||
| 1ef741563d | |||
| 2d44a681eb | |||
| 543af651d0 | |||
| e48666aeaf | |||
| 87f80379ea | |||
| 9cee054920 | |||
| e69f35507e | |||
| 379b8d9583 | |||
| bf79770706 | |||
| 7b0a865c97 | |||
| ce13cfd30a | |||
| c51fbcb7de | |||
| 796f7ab414 | |||
| 94d34beed4 | |||
| f476060de6 | |||
| 6a953bb0cb | |||
| 3092fd31bb | |||
| dae4a2e736 | |||
| 2632250a42 | |||
| fdbd92ff99 | |||
| 3b27dd37a2 | |||
| 76c6493c2b | |||
| da03497f6b | |||
| a1fd2239c9 | |||
| a39baef657 | |||
| 56508a1d5f | |||
| d7d8708a9b | |||
| 2ffbfb0ad0 | |||
| 946d9ae90b | |||
| a84393eadb | |||
| c429ad82f5 | |||
| 900c7cd923 | |||
| 2d3303ddda | |||
| a985adfdab | |||
| ca41996ef7 | |||
| 9a60408c80 | |||
| 09f6061a8e | |||
| 2d1ee4bffd | |||
| a412622f69 | |||
| 63ffe5c360 | |||
| 2fc954d33a | |||
| 512c081187 | |||
| 2563ff7ba9 | |||
| 0a641c0181 | |||
| da04571dbc | |||
| 7f1cd3e6a2 | |||
| 6d85f24e20 | |||
| 0afd775433 | |||
| 8eb01216b4 | |||
| 323f9ab736 | |||
| 5ba4419e26 | |||
| 529b2507bd | |||
| 6d19aa2e7c | |||
| bf35d53855 | |||
| 6c4581fcb6 | |||
| 620a20d1b3 | |||
| 1f8431bfaa | |||
| b8773a71b5 | |||
| 8a63071ab0 | |||
| e2a0368726 | |||
| d9157849df | |||
| d558dc5f95 | |||
| 616695eb05 | |||
| 9ac4e5a64b | |||
| fcb6e1e96b | |||
| cf972fd0bd | |||
| ffc3115705 | |||
| 9590bcf062 | |||
| 689c01f599 | |||
| 2334a5101d | |||
| ed7777d3e1 | |||
| a1648737bd | |||
| 420586bd09 | |||
| d89afc488f | |||
| 0a8a6fa36b | |||
| e1a182b105 | |||
| 214f6822c3 | |||
| 9c5a1cd43c | |||
| 8120ab2f77 | |||
| 2f08eae48f | |||
| 6610ae816f | |||
| fa56f8eadb | |||
| 7882c1c6ae | |||
| 84a6ef4d21 | |||
| 4d680c3fad | |||
| bab3069dff | |||
| 4cb8ac61c7 | |||
| 9d28a79219 | |||
| c4489eb3cb | |||
| 7caf91300f | |||
| 0c1b54f04c | |||
| 85e392f354 | |||
| c98e08fd87 | |||
| 33cc75e6fc | |||
| 474865242c | |||
| 0292e6a1a8 | |||
| de193422a3 | |||
| ff228fb524 | |||
| 8709539d4c | |||
| 2995b54afd | |||
| e26256fb70 | |||
| e45f9b66fa | |||
| 158f1aec86 | |||
| f53a7f62bb | |||
| d2b08a0465 | |||
| 43c4029c8a | |||
| 55ac985373 | |||
| 37b6ed3225 | |||
| c5bf9daef6 | |||
| be01cebfae | |||
| 7a81e6fe2d | |||
| 9c49eb131a | |||
| 374a302b90 | |||
| b3da88077f | |||
| 6454f51741 | |||
| e509ab5be6 | |||
| 592bf516e5 | |||
| 8cdafe46e3 | |||
| 45c5688d42 | |||
| 7bb2414bf6 | |||
| da11c1bcee | |||
| a80697938e | |||
| 1d9ad76f1f | |||
| 01387ba3c3 | |||
| f163c90555 | |||
| 4e94864e54 | |||
| 54c4041ebc | |||
| f780aba434 | |||
| 057f78de8b | |||
| 7a36128efc | |||
| d2cd8b9bb6 | |||
| 937942f5ad | |||
| 75e876424d | |||
| 19fab4a1d7 | |||
| d1293540e1 | |||
| 22f66025db | |||
| 6f8ddb6d43 | |||
| 34590e15d4 | |||
| 83098b9add | |||
| 5d8861fb2f | |||
| b9f7c453e0 | |||
| 750344129e | |||
| 74da034077 | |||
| 91ef94d284 | |||
| ab9b021572 | |||
| b268c0b7b4 | |||
| b0c19f8b06 | |||
| bbc2a0ae48 | |||
| ca53dfcc18 | |||
| ce6a96b0d7 | |||
| d4b359f4b2 | |||
| 8d841c3405 | |||
| 2b285c75f4 | |||
| 6e4464331d | |||
| 2f8db1bf01 | |||
| 838cf4be3c | |||
| de2a56bbc8 | |||
| 55ad192e4a | |||
| 5b4713e43e | |||
| 3fa9aba0cc | |||
| 1bba358a75 | |||
| 7a4124c298 | |||
| 2512a81e09 | |||
| 44c9d1616a | |||
| 5758d73964 | |||
| 6bd6dbff49 | |||
| 7170f9d9ca | |||
| 1c0f721368 | |||
| 2a5a52a76c | |||
| c690946469 | |||
| 87b0055c80 | |||
| 2116857a2a | |||
| 0f58334b7b | |||
| bcc257b459 | |||
| 980fb395e4 | |||
| 62ed26a84f | |||
| 59f1f4e19a | |||
| cb51116dbd | |||
| c1f34e8eeb | |||
| 7bf5429e3b | |||
| d3da55c40f | |||
| 70edec947c | |||
| fe17c0e066 | |||
| e403682444 | |||
| 27d441b0d6 | |||
| 8a944b0872 | |||
| 786a1a4429 | |||
| 8e5c4e92f7 | |||
| 46d24ae4c8 | |||
| 9dd33c09b1 | |||
| fea8240c81 | |||
| 3d2b1be211 | |||
| f08a0c5ad1 | |||
| 6f1e0ba563 | |||
| 540338f9a5 | |||
| 0e6a700807 | |||
| 4fc40bc932 | |||
| 216724b4cb | |||
| 9bd1645970 | |||
| 3397a031a1 | |||
| 5ec5aa7751 | |||
| bf5ac5261d | |||
| 91b7cd9b74 | |||
| 7b2ecf42c6 | |||
| 256d9a948c | |||
| 99fc6cda98 | |||
| 690b69b9cd | |||
| 4262f15e16 | |||
| 3a8d1354ce | |||
| f9387c6890 | |||
| 48d0ffcbc4 | |||
| 3485ba1e2b | |||
| 2f61145475 | |||
| 8c618d896b | |||
| 68d4dc5b71 | |||
| 03a4a96cf9 | |||
| 655c52a621 | |||
| fa3ddba5f2 | |||
| c4a1b6124e | |||
| e52d731bfd | |||
| 9b72843018 | |||
| 9c1f8ea70b | |||
| 9fde5648e4 | |||
| ea829620b2 | |||
| 1731d091f8 | |||
| 9d3704ca46 | |||
| 215dff34dd | |||
| fa8c399fad | |||
| 7295c60ffb | |||
| fa01571036 | |||
| dbc698517f | |||
| a7f3761eda | |||
| 5a98e806ef | |||
| 808f984ec0 | |||
| 698af191de | |||
| 7a413df5e4 | |||
| 4994acd26e |
@@ -1,27 +0,0 @@
|
||||
***Note*: for support questions, please use one of these channels: https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#question. This repository's issues are reserved for feature requests and bug reports.**
|
||||
|
||||
**Do you want to request a *feature* or report a *bug*?**
|
||||
|
||||
|
||||
|
||||
**What is the current behavior?**
|
||||
|
||||
|
||||
|
||||
**If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar (template: http://plnkr.co/edit/tpl:yBpEi4).**
|
||||
|
||||
|
||||
|
||||
**What is the expected behavior?**
|
||||
|
||||
|
||||
|
||||
**What is the motivation / use case for changing the behavior?**
|
||||
|
||||
|
||||
|
||||
**Which versions of Angular, and which browser / OS are affected by this issue? Did this work in previous versions of Angular? Please also test with the latest stable and snapshot (https://code.angularjs.org/snapshot/) versions.**
|
||||
|
||||
|
||||
|
||||
**Other information (e.g. stacktraces, related issues, suggestions how to fix)**
|
||||
@@ -1,23 +0,0 @@
|
||||
**What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)**
|
||||
|
||||
|
||||
|
||||
**What is the current behavior? (You can also link to an open issue here)**
|
||||
|
||||
|
||||
|
||||
**What is the new behavior (if this is a feature change)?**
|
||||
|
||||
|
||||
|
||||
**Does this PR introduce a breaking change?**
|
||||
|
||||
|
||||
|
||||
**Please check if the PR fulfills these requirements**
|
||||
- [ ] The commit message follows our guidelines: https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format
|
||||
- [ ] Tests for the changes have been added (for bug fixes / features)
|
||||
- [ ] Docs have been added / updated (for bug fixes / features)
|
||||
|
||||
**Other information**:
|
||||
|
||||
@@ -62,7 +62,6 @@ notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://webhooks.gitter.im/e/d2120f3f2bb39a4531b2
|
||||
- http://104.197.9.155:8484/hubot/travis/activity #hubot-server
|
||||
on_success: always # options: [always|never|change] default: always
|
||||
on_success: change # options: [always|never|change] default: always
|
||||
on_failure: always # options: [always|never|change] default: always
|
||||
on_start: always # default: false
|
||||
on_start: false # default: false
|
||||
|
||||
@@ -193,8 +193,6 @@ We have very precise rules over how our git commit messages can be formatted. T
|
||||
readable messages** that are easy to follow when looking through the **project history**. But also,
|
||||
we use the git commit messages to **generate the AngularJS change log**.
|
||||
|
||||
The commit message formatting can be added using a typical git workflow or through the use of a CLI wizard ([Commitizen](https://github.com/commitizen/cz-cli)). To use the wizard, run `npm run commit` in your terminal after staging your changes in git.
|
||||
|
||||
### Commit Message Format
|
||||
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
|
||||
format that includes a **type**, a **scope** and a **subject**:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
Copyright (c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -26,7 +26,7 @@ Building AngularJS
|
||||
grunt package
|
||||
|
||||
|
||||
Running tests
|
||||
Running Tests
|
||||
-------------
|
||||
To execute all unit tests, use:
|
||||
|
||||
@@ -43,7 +43,7 @@ To learn more about the grunt tasks, run `grunt --help` and also read our
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
What to use AngularJS for and when to use it
|
||||
What to Use AngularJS for and When to Use it
|
||||
---------
|
||||
AngularJS is the next generation framework where each component is designed to work with every other component in an interconnected way like a well-oiled machine. AngularJS is JavaScript MVC made easy and done right. (Well it is not really MVC, read on, to understand what this means.)
|
||||
|
||||
@@ -61,8 +61,8 @@ Data and Data Models in AngularJS are plain JavaScript objects and one can add a
|
||||
#### Two-way Data Binding
|
||||
One of AngularJS's strongest features. Two-way Data Binding means that if something changes in the Model, the change gets reflected in the View instantaneously, and the same happens the other way around. This is also referred to as Reactive Programming, i.e. suppose `a = b + c` is being programmed and after this, if the value of `b` and/or `c` is changed then the value of `a` will be automatically updated to reflect the change. AngularJS uses its "scopes" as a glue between the Model and View and makes these updates in one available for the other.
|
||||
|
||||
#### Less Written Code and Easily Maintainable Code
|
||||
Everything in AngularJS is created to enable the programmer to end up writing less code that is easily maintainable and readable by any other new person on the team. Believe it or not, one can write a complete working two-way data binded application in less than 10 lines of code. Try and see for yourself!
|
||||
#### Less Written Code and Easily Maintable Code
|
||||
Everything in AngularJS is created to enable the programmer ends up writing less code that is easily maintainable and readable by any other new person on the team. Believe it or not, one can write a complete working two-way data binded application in less than 10 lines of code. Try and see for yourself!
|
||||
|
||||
#### Testing Ready
|
||||
AngularJS has Dependency Injection, i.e. it takes care of providing all the necessary dependencies to its controllers whenever required. This helps in making the AngularJS code ready for unit testing by making use of mock dependencies created and injected. This makes AngularJS more modular and easily testable thus in turn helping a team create more robust applications.
|
||||
|
||||
@@ -20,7 +20,7 @@ The following is done automatically so you don't have to worry about it:
|
||||
This process based on the idea of minimizing user pain
|
||||
[from this blog post](http://www.lostgarden.com/2008/05/improving-bug-triage-with-user-pain.html).
|
||||
|
||||
1. Open the list of [non triaged issues](https://github.com/angular/angular.js/issues?q=is%3Aopen+sort%3Acreated-desc+no%3Amilestone)
|
||||
1. Open the list of [non triaged issues](https://github.com/angular/angular.js/issues?direction=desc&milestone=none&page=1&sort=created&state=open)
|
||||
* Sort by submit date, with the newest issues first
|
||||
* You don't have to do issues in order; feel free to pick and choose issues as you please.
|
||||
* You can triage older issues as well
|
||||
|
||||
@@ -102,7 +102,6 @@ var angularFiles = {
|
||||
'src/ngAnimate/animateJsDriver.js',
|
||||
'src/ngAnimate/animateQueue.js',
|
||||
'src/ngAnimate/animation.js',
|
||||
'src/ngAnimate/ngAnimateSwap.js',
|
||||
'src/ngAnimate/module.js'
|
||||
],
|
||||
'ngCookies': [
|
||||
@@ -205,7 +204,6 @@ var angularFiles = {
|
||||
"karmaModules": [
|
||||
'build/angular.js',
|
||||
'@angularSrcModules',
|
||||
'test/modules/no_bootstrap.js',
|
||||
'src/ngScenario/browserTrigger.js',
|
||||
'test/helpers/*.js',
|
||||
'test/ngMessageFormat/*.js',
|
||||
|
||||
@@ -8,20 +8,20 @@
|
||||
Large table rendered with AngularJS
|
||||
</p>
|
||||
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="none">none: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="baselineBinding">baseline binding: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="baselineInterpolation">baseline interpolation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngBind">ngBind: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngBindOnce">ngBindOnce: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="interpolation">interpolation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="bindOnceInterpolation">interpolation + bind-once: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="interpolationAttr">attribute interpolation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngBindFn">ngBind + fnInvocation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="interpolationFn">interpolation + fnInvocation: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngBindFilter">ngBind + filter: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="interpolationFilter">interpolation + filter: </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngModelConstName">ngModel (const name): </label></div>
|
||||
<div><label><input type="radio" ng-model="benchmarkType" value="ngModelInterpName">ngModel (interp name): </label></div>
|
||||
<div>none: <input type="radio" ng-model="benchmarkType" value="none"></div>
|
||||
<div>baseline binding: <input type="radio" ng-model="benchmarkType" value="baselineBinding"></div>
|
||||
<div>baseline interpolation: <input type="radio" ng-model="benchmarkType" value="baselineInterpolation"></div>
|
||||
<div>ngBind: <input type="radio" ng-model="benchmarkType" value="ngBind"></div>
|
||||
<div>ngBindOnce: <input type="radio" ng-model="benchmarkType" value="ngBindOnce"></div>
|
||||
<div>interpolation: <input type="radio" ng-model="benchmarkType" value="interpolation"></div>
|
||||
<div>interpolation + bind-once: <input type="radio" ng-model="benchmarkType" value="bindOnceInterpolation"></div>
|
||||
<div>attribute interpolation: <input type="radio" ng-model="benchmarkType" value="interpolationAttr"></div>
|
||||
<div>ngBind + fnInvocation: <input type="radio" ng-model="benchmarkType" value="ngBindFn"></div>
|
||||
<div>interpolation + fnInvocation: <input type="radio" ng-model="benchmarkType" value="interpolationFn"></div>
|
||||
<div>ngBind + filter: <input type="radio" ng-model="benchmarkType" value="ngBindFilter"></div>
|
||||
<div>interpolation + filter: <input type="radio" ng-model="benchmarkType" value="interpolationFilter"></div>
|
||||
<div>ngModel (const name): <input type="radio" ng-model="benchmarkType" value="ngModelConstName"></div>
|
||||
<div>ngModel (interp name): <input type="radio" ng-model="benchmarkType" value="ngModelInterpName"></div>
|
||||
|
||||
<ng-switch on="benchmarkType">
|
||||
<baseline-binding-table ng-switch-when="baselineBinding">
|
||||
|
||||
@@ -315,13 +315,8 @@ iframe.example {
|
||||
color:white;
|
||||
}
|
||||
|
||||
.search-results-group .search-results {
|
||||
padding: 0 5px 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.search-results-frame > .search-results-group:first-child > .search-results {
|
||||
border-right:1px solid #222;
|
||||
border-right:1px solid #050505;
|
||||
}
|
||||
|
||||
.search-results-group.col-group-api { width:30%; }
|
||||
@@ -330,57 +325,10 @@ iframe.example {
|
||||
.search-results-group.col-group-misc,
|
||||
.search-results-group.col-group-error { width:15%; float: right; }
|
||||
|
||||
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
|
||||
.search-results-group.col-group-api .search-results {
|
||||
-moz-column-count: 2;
|
||||
-ms-column-count: 2;
|
||||
-webkit-column-count: 2;
|
||||
column-count: 2;
|
||||
/* Prevent bullets in the second column from being hidden in Chrome and IE */
|
||||
-webkit-column-gap: 2em;
|
||||
-ms-column-gap: 2em;
|
||||
column-gap: 2em;
|
||||
}
|
||||
}
|
||||
|
||||
.search-results-group .search-result {
|
||||
word-wrap: break-word;
|
||||
-webkit-hyphens: auto;
|
||||
-moz-hyphens: auto;
|
||||
-ms-hyphens: auto;
|
||||
hyphens: auto;
|
||||
-ms-column-break-inside: avoid;
|
||||
-webkit-column-break-inside: avoid;
|
||||
-moz-column-break-inside: avoid; /* Unsupported */
|
||||
column-break-inside: avoid;
|
||||
text-indent: -0.65em; /* Make sure line wrapped words are aligned vertically */
|
||||
}
|
||||
|
||||
@supports (-moz-column-count: 2) {
|
||||
.search-results-group .search-result {
|
||||
/* Prevents column breaks inside words in FF, but has adverse effects in IE11 and Chrome */
|
||||
overflow: hidden;
|
||||
padding-left: 1em; /* In FF the list item bullet is otherwise hidden */
|
||||
margin-left: -1em; /* offset the padding left */
|
||||
}
|
||||
}
|
||||
|
||||
.search-result:before {
|
||||
content: "\002D\00A0"; /* Dash and non-breaking space as List item type */
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search-results-group.col-group-api .search-result {
|
||||
width:48%;
|
||||
display:inline-block;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
|
||||
.search-results-group.col-group-api .search-result {
|
||||
width:auto;
|
||||
display: list-item;
|
||||
}
|
||||
}
|
||||
|
||||
.search-close {
|
||||
@@ -740,11 +688,6 @@ ul.events > li {
|
||||
padding-bottom:60px;
|
||||
text-align:left;
|
||||
}
|
||||
|
||||
.search-results-frame > .search-results-group:first-child > .search-results {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.search-results-group {
|
||||
float:none!important;
|
||||
display:block!important;
|
||||
@@ -752,42 +695,14 @@ ul.events > li {
|
||||
border:0!important;
|
||||
padding:0!important;
|
||||
}
|
||||
|
||||
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
|
||||
.search-results-group .search-results {
|
||||
-moz-column-count: 2;
|
||||
-ms-column-count: 2;
|
||||
-webkit-column-count: 2;
|
||||
column-count: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.search-results-group .search-result {
|
||||
display:inline-block!important;
|
||||
padding:0 5px;
|
||||
width:auto!important;
|
||||
text-indent: initial;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.search-results-group .search-result:after {
|
||||
content:", ";
|
||||
}
|
||||
|
||||
.search-results-group .search-result:before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
@supports ((column-count: 2) or (-moz-column-count: 2) or (-ms-column-count: 2) or (-webkit-column-count: 2)) {
|
||||
.search-results-group .search-result {
|
||||
display: list-item !important;
|
||||
}
|
||||
|
||||
.search-results-group .search-result:after {
|
||||
content: "";
|
||||
}
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
padding-bottom:0px;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/* global importScripts, onmessage: true, postMessage, lunr */
|
||||
|
||||
// Load up the lunr library
|
||||
importScripts('../components/lunr.js-0.5.12/lunr.min.js');
|
||||
importScripts('../components/lunr.js-0.4.2/lunr.min.js');
|
||||
|
||||
// Create the lunr index - the docs should be an array of object, each object containing
|
||||
// the path and search terms for a page
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
angular.module('DocsController', [])
|
||||
|
||||
.controller('DocsController', [
|
||||
'$scope', '$rootScope', '$location', '$window', '$cookies',
|
||||
'$scope', '$rootScope', '$location', '$window', '$cookies', 'openPlunkr',
|
||||
'NG_PAGES', 'NG_NAVIGATION', 'NG_VERSION',
|
||||
function($scope, $rootScope, $location, $window, $cookies,
|
||||
function($scope, $rootScope, $location, $window, $cookies, openPlunkr,
|
||||
NG_PAGES, NG_NAVIGATION, NG_VERSION) {
|
||||
|
||||
$scope.openPlunkr = openPlunkr;
|
||||
|
||||
$scope.docsVersion = NG_VERSION.isSnapshot ? 'snapshot' : NG_VERSION.version;
|
||||
|
||||
$scope.navClass = function(navItem) {
|
||||
|
||||
@@ -72,110 +72,29 @@ angular.module('examples', [])
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('createCopyrightNotice', function() {
|
||||
var COPYRIGHT = 'Copyright ' + (new Date()).getFullYear() + ' Google Inc. All Rights Reserved.\n'
|
||||
+ 'Use of this source code is governed by an MIT-style license that\n'
|
||||
+ 'can be found in the LICENSE file at http://angular.io/license';
|
||||
var COPYRIGHT_JS_CSS = '\n\n/*\n' + COPYRIGHT + '\n*/';
|
||||
var COPYRIGHT_HTML = '\n\n<!-- \n' + COPYRIGHT + '\n-->';
|
||||
|
||||
return function getCopyright(filename) {
|
||||
switch (filename.substr(filename.lastIndexOf('.'))) {
|
||||
case '.html':
|
||||
return COPYRIGHT_HTML;
|
||||
case '.js':
|
||||
case '.css':
|
||||
return COPYRIGHT_JS_CSS;
|
||||
case '.md':
|
||||
return COPYRIGHT;
|
||||
}
|
||||
return '';
|
||||
};
|
||||
})
|
||||
.factory('openPlunkr', ['formPostData', '$http', '$q', function(formPostData, $http, $q) {
|
||||
return function(exampleFolder, clickEvent) {
|
||||
|
||||
.directive('plnkrOpener', ['$q', 'getExampleData', 'formPostData', 'createCopyrightNotice', function($q, getExampleData, formPostData, createCopyrightNotice) {
|
||||
return {
|
||||
scope: {},
|
||||
bindToController: {
|
||||
'examplePath': '@'
|
||||
},
|
||||
controllerAs: 'plnkr',
|
||||
template: '<button ng-click="plnkr.open($event)" class="btn pull-right"> <i class="glyphicon glyphicon-edit"> </i> Edit in Plunker</button> ',
|
||||
controller: [function() {
|
||||
var ctrl = this;
|
||||
var exampleName = 'AngularJS Example';
|
||||
var newWindow = clickEvent.ctrlKey || clickEvent.metaKey;
|
||||
|
||||
ctrl.example = {
|
||||
path: ctrl.examplePath,
|
||||
manifest: undefined,
|
||||
files: undefined,
|
||||
name: 'AngularJS Example'
|
||||
};
|
||||
|
||||
ctrl.prepareExampleData = function() {
|
||||
if (ctrl.example.manifest) {
|
||||
return $q.when(ctrl.example);
|
||||
}
|
||||
|
||||
return getExampleData(ctrl.examplePath).then(function(data) {
|
||||
ctrl.example.files = data.files;
|
||||
ctrl.example.manifest = data.manifest;
|
||||
|
||||
// Build a pretty title for the Plunkr
|
||||
var exampleNameParts = data.manifest.name.split('-');
|
||||
exampleNameParts.unshift('AngularJS');
|
||||
angular.forEach(exampleNameParts, function(part, index) {
|
||||
exampleNameParts[index] = part.charAt(0).toUpperCase() + part.substr(1);
|
||||
});
|
||||
ctrl.example.name = exampleNameParts.join(' - ');
|
||||
|
||||
return ctrl.example;
|
||||
});
|
||||
};
|
||||
|
||||
ctrl.open = function(clickEvent) {
|
||||
|
||||
var newWindow = clickEvent.ctrlKey || clickEvent.metaKey;
|
||||
|
||||
var postData = {
|
||||
'tags[0]': "angularjs",
|
||||
'tags[1]': "example",
|
||||
'private': true
|
||||
};
|
||||
|
||||
// Make sure the example data is available.
|
||||
// If an XHR must be made, this might break some pop-up blockers when
|
||||
// new window is requested
|
||||
ctrl.prepareExampleData()
|
||||
.then(function() {
|
||||
angular.forEach(ctrl.example.files, function(file) {
|
||||
postData['files[' + file.name + ']'] = file.content + createCopyrightNotice(file.name);
|
||||
});
|
||||
|
||||
postData.description = ctrl.example.name;
|
||||
|
||||
formPostData('http://plnkr.co/edit/?p=preview', newWindow, postData);
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
// Initialize the example data, so it's ready when clicking the open button.
|
||||
// Otherwise pop-up blockers will prevent a new window from opening
|
||||
ctrl.prepareExampleData(ctrl.example.path);
|
||||
|
||||
}]
|
||||
};
|
||||
}])
|
||||
|
||||
.factory('getExampleData', ['$http', '$q', function($http, $q) {
|
||||
return function(exampleFolder){
|
||||
// Load the manifest for the example
|
||||
return $http.get(exampleFolder + '/manifest.json')
|
||||
$http.get(exampleFolder + '/manifest.json')
|
||||
.then(function(response) {
|
||||
return response.data;
|
||||
})
|
||||
.then(function(manifest) {
|
||||
var filePromises = [];
|
||||
|
||||
// Build a pretty title for the Plunkr
|
||||
var exampleNameParts = manifest.name.split('-');
|
||||
exampleNameParts.unshift('AngularJS');
|
||||
angular.forEach(exampleNameParts, function(part, index) {
|
||||
exampleNameParts[index] = part.charAt(0).toUpperCase() + part.substr(1);
|
||||
});
|
||||
exampleName = exampleNameParts.join(' - ');
|
||||
|
||||
angular.forEach(manifest.files, function(filename) {
|
||||
filePromises.push($http.get(exampleFolder + '/' + filename, { transformResponse: [] })
|
||||
.then(function(response) {
|
||||
@@ -183,7 +102,7 @@ angular.module('examples', [])
|
||||
// The manifests provide the production index file but Plunkr wants
|
||||
// a straight index.html
|
||||
if (filename === "index-production.html") {
|
||||
filename = "index.html";
|
||||
filename = "index.html"
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -192,11 +111,21 @@ angular.module('examples', [])
|
||||
};
|
||||
}));
|
||||
});
|
||||
return $q.all(filePromises);
|
||||
})
|
||||
.then(function(files) {
|
||||
var postData = {};
|
||||
|
||||
return $q.all({
|
||||
manifest: manifest,
|
||||
files: $q.all(filePromises)
|
||||
angular.forEach(files, function(file) {
|
||||
postData['files[' + file.name + ']'] = file.content;
|
||||
});
|
||||
|
||||
postData['tags[0]'] = "angularjs";
|
||||
postData['tags[1]'] = "example";
|
||||
postData.private = true;
|
||||
postData.description = exampleName;
|
||||
|
||||
formPostData('http://plnkr.co/edit/?p=preview', newWindow, postData);
|
||||
});
|
||||
};
|
||||
}]);
|
||||
}]);
|
||||
|
||||
@@ -11,15 +11,7 @@ angular.module('search', [])
|
||||
var MIN_SEARCH_LENGTH = 2;
|
||||
if(q.length >= MIN_SEARCH_LENGTH) {
|
||||
docsSearch(q).then(function(hits) {
|
||||
// Make sure the areas are always in the same order
|
||||
var results = {
|
||||
api: [],
|
||||
guide: [],
|
||||
tutorial: [],
|
||||
error: [],
|
||||
misc: []
|
||||
};
|
||||
|
||||
var results = {};
|
||||
angular.forEach(hits, function(hit) {
|
||||
var area = hit.area;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ describe("DocsController", function() {
|
||||
|
||||
angular.module('fake', [])
|
||||
.value('$cookies', {})
|
||||
.value('openPlunkr', function() {})
|
||||
.value('NG_PAGES', {})
|
||||
.value('NG_NAVIGATION', {})
|
||||
.value('NG_VERSION', {});
|
||||
@@ -25,7 +26,7 @@ describe("DocsController", function() {
|
||||
|
||||
it("should update the Google Analytics with $location.path if currentPage is missing", inject(function($window, $location) {
|
||||
$window._gaq = [];
|
||||
spyOn($location, 'path').and.returnValue('x/y/z');
|
||||
spyOn($location, 'path').andReturn('x/y/z');
|
||||
$scope.$broadcast('$includeContentLoaded');
|
||||
expect($window._gaq.pop()).toEqual(['_trackPageview', 'x/y/z']);
|
||||
}));
|
||||
|
||||
@@ -4,7 +4,7 @@ describe('errors', function() {
|
||||
// Mock `ngSanitize` module
|
||||
angular.
|
||||
module('ngSanitize', []).
|
||||
value('$sanitize', jasmine.createSpy('$sanitize').and.callFake(angular.identity));
|
||||
value('$sanitize', jasmine.createSpy('$sanitize').andCallFake(angular.identity));
|
||||
|
||||
beforeEach(module('errors'));
|
||||
|
||||
@@ -103,12 +103,12 @@ describe('errors', function() {
|
||||
|
||||
|
||||
it('should pass the final string through `$sanitize`', function() {
|
||||
$sanitize.calls.reset();
|
||||
$sanitize.reset();
|
||||
|
||||
var input = 'start https://foo/bar?baz#qux end';
|
||||
var output = errorLinkFilter(input);
|
||||
|
||||
expect($sanitize).toHaveBeenCalledTimes(1);
|
||||
expect($sanitize.callCount).toBe(1);
|
||||
expect($sanitize).toHaveBeenCalledWith(output);
|
||||
});
|
||||
});
|
||||
@@ -123,7 +123,7 @@ describe('errors', function() {
|
||||
beforeEach(module(function($provide) {
|
||||
$provide.decorator('errorLinkFilter', function() {
|
||||
errorLinkFilter = jasmine.createSpy('errorLinkFilter');
|
||||
errorLinkFilter.and.callFake(angular.identity);
|
||||
errorLinkFilter.andCallFake(angular.identity);
|
||||
|
||||
return errorLinkFilter;
|
||||
});
|
||||
@@ -142,7 +142,7 @@ describe('errors', function() {
|
||||
|
||||
|
||||
it('should interpolate the contents against `$location.search()`', function() {
|
||||
spyOn($location, 'search').and.returnValue({p0: 'foo', p1: 'bar'});
|
||||
spyOn($location, 'search').andReturn({p0: 'foo', p1: 'bar'});
|
||||
|
||||
var elem = $compile('<span error-display="foo = {0}, bar = {1}"></span>')($rootScope);
|
||||
expect(elem.html()).toBe('foo = foo, bar = bar');
|
||||
@@ -150,10 +150,10 @@ describe('errors', function() {
|
||||
|
||||
|
||||
it('should pass the interpolated text through `errorLinkFilter`', function() {
|
||||
$location.search = jasmine.createSpy('search').and.returnValue({p0: 'foo'});
|
||||
$location.search = jasmine.createSpy('search').andReturn({p0: 'foo'});
|
||||
|
||||
var elem = $compile('<span error-display="foo = {0}"></span>')($rootScope);
|
||||
expect(errorLinkFilter).toHaveBeenCalledTimes(1);
|
||||
expect(errorLinkFilter.callCount).toBe(1);
|
||||
expect(errorLinkFilter).toHaveBeenCalledWith('foo = foo', '_blank');
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "AngularJS-docs-app",
|
||||
"dependencies": {
|
||||
"jquery": "2.1.1",
|
||||
"lunr.js": "0.5.12",
|
||||
"lunr.js": "0.4.3",
|
||||
"open-sans-fontface": "1.0.4",
|
||||
"google-code-prettify": "1.0.1",
|
||||
"bootstrap": "3.1.1"
|
||||
|
||||
@@ -147,13 +147,13 @@
|
||||
<div class="search-results-container" ng-show="hasResults">
|
||||
<div class="container">
|
||||
<div class="search-results-frame">
|
||||
<div ng-repeat="(key, value) in results track by key" class="search-results-group" ng-class="colClassName + ' col-group-' + key" ng-show="value.length > 0">
|
||||
<div ng-repeat="(key, value) in results" class="search-results-group" ng-class="colClassName + ' col-group-' + key">
|
||||
<h4 class="search-results-group-heading">{{ key }}</h4>
|
||||
<ul class="search-results">
|
||||
<!-- Do not insert a line break between li and a. Chrome will insert an actual line-break, which breaks the list item view.
|
||||
TODO: use a html minifier instead -->
|
||||
<li ng-repeat="item in value" class="search-result"><a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a></li>
|
||||
</ul>
|
||||
<div class="search-results">
|
||||
<div ng-repeat="item in value" class="search-result">
|
||||
- <a ng-click="hideResults()" ng-href="{{ item.path }}">{{ item.name }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="" ng-click="hideResults()" class="search-close">
|
||||
@@ -220,7 +220,7 @@
|
||||
<p class="pull-right"><a back-to-top>Back to top</a></p>
|
||||
|
||||
<p>
|
||||
Super-powered by Google ©2010-2016
|
||||
Super-powered by Google ©2010-2015
|
||||
( <a id="version"
|
||||
ng-href="https://github.com/angular/angular.js/blob/master/CHANGELOG.md#{{versionNumber}}"
|
||||
ng-bind-template="v{{version}}" title="Changelog of this version of Angular JS">
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
is HTML and wrap each line in a <p> - thus breaking the HTML #}
|
||||
|
||||
<div>
|
||||
<plnkr-opener example-path="{$ doc.path $}"></plnkr-opener>
|
||||
<a ng-click="openPlunkr('{$ doc.path $}', $event)" class="btn pull-right">
|
||||
<i class="glyphicon glyphicon-edit"> </i>
|
||||
Edit in Plunker</a>
|
||||
|
||||
<div class="runnable-example"
|
||||
path="{$ doc.example.deployments.default.path $}"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
@fullName Invalid Isolate Scope Definition
|
||||
@description
|
||||
|
||||
When declaring isolate scope the scope definition object must be in specific format which starts with mode character (`@&=<`), after which comes an optional `?`, and it ends with an optional local name.
|
||||
When declaring isolate scope the scope definition object must be in specific format which starts with mode character (`@&=`) with an optional local name.
|
||||
|
||||
```
|
||||
myModule.directive('directiveName', function factory() {
|
||||
@@ -12,7 +12,7 @@ myModule.directive('directiveName', function factory() {
|
||||
scope: {
|
||||
'attrName': '@', // OK
|
||||
'attrName2': '=localName', // OK
|
||||
'attrName3': '<?localName', // OK
|
||||
'attrName3': '&?localName', // OK
|
||||
'attrName4': ' = name', // OK
|
||||
'attrName5': 'name', // ERROR: missing mode @&=
|
||||
'attrName6': 'name=', // ERROR: must be prefixed with @&=
|
||||
|
||||
@@ -31,7 +31,7 @@ single root element, like the `div` element in this template:
|
||||
<div><b>Hello</b> World!</div>
|
||||
```
|
||||
|
||||
An invalid template to be used with this directive is one that defines multiple root nodes or
|
||||
An an invalid template to be used with this directive is one that defines multiple root nodes or
|
||||
elements. For example:
|
||||
|
||||
```
|
||||
@@ -43,7 +43,7 @@ well. Consider the following template:
|
||||
|
||||
```
|
||||
<div class='container'>
|
||||
<div class='wrapper'>
|
||||
<div class='wrapper>
|
||||
...
|
||||
</div> <!-- wrapper -->
|
||||
</div> <!-- container -->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@ngdoc error
|
||||
@name $location:nobase
|
||||
@fullName $location in HTML5 mode requires a <base> tag to be present!
|
||||
@fullName $location in HTML5 mode requires a `<base>` tag to be present!
|
||||
@description
|
||||
|
||||
If you configure {@link ng.$location `$location`} to use
|
||||
|
||||
@@ -14,32 +14,3 @@ perform this check - it's up to the developer to not expose such sensitive and p
|
||||
directly on the scope chain.
|
||||
|
||||
To resolve this error, avoid Window access.
|
||||
|
||||
### Common CoffeeScript Issue
|
||||
|
||||
Be aware that if you are using CoffeeScript, it automatically returns the value of the last statement in a
|
||||
function. So for instance
|
||||
|
||||
```coffeescript
|
||||
scope.foo = ->
|
||||
window.open 'https://example.com'
|
||||
```
|
||||
|
||||
compiles to something like
|
||||
|
||||
```js
|
||||
scope.foo = function() {
|
||||
return window.open('https://example.com');
|
||||
};
|
||||
```
|
||||
|
||||
You can see that this function will return the result of calling `window.open`, which is a `Window`
|
||||
object.
|
||||
|
||||
You can avoid this by explicitly returning something else from the function:
|
||||
|
||||
```coffeescript
|
||||
scope.foo = ->
|
||||
window.open 'https://example.com'
|
||||
return true;
|
||||
```
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
@ngdoc error
|
||||
@name $sanitize:badparse
|
||||
@fullName Parsing Error while Sanitizing
|
||||
@description
|
||||
|
||||
This error occurs when the HTML string passed to '$sanitize' can't be parsed by the sanitizer.
|
||||
The error contains part of the html string that can't be parsed.
|
||||
|
||||
The parser is more strict than a typical browser parser, so it's possible that some obscure input would produce this error despite the string being recognized as valid HTML by a browser.
|
||||
|
||||
If a valid html code results in this error, please file a bug.
|
||||
@@ -1,10 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $sanitize:noinert
|
||||
@fullName Can't create an inert html document
|
||||
@description
|
||||
|
||||
This error occurs when `$sanitize` sanitizer determines that `document.implementation.createHTMLDocument ` api is not supported by the current browser.
|
||||
|
||||
This api is necessary for safe parsing of HTML strings into DOM trees and without it the sanitizer can't sanitize the input.
|
||||
|
||||
The api is present in all supported browsers including IE 9.0, so the presence of this error usually indicates that Angular's `$sanitize` is being used on an unsupported platform.
|
||||
@@ -1,13 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $sanitize:uinput
|
||||
@fullName Failed to sanitize html because the input is unstable
|
||||
@description
|
||||
|
||||
This error occurs when `$sanitize` sanitizer tries to check the input for possible mXSS payload and the verification
|
||||
errors due to the input mutating indefinitely. This could be a sign that the payload contains code exploiting an mXSS
|
||||
vulnerability in the browser.
|
||||
|
||||
mXSS attack exploit browser bugs that cause some browsers parse a certain html strings into DOM, which once serialized
|
||||
doesn't match the original input. These browser bugs can be exploited by attackers to create payload which looks
|
||||
harmless to sanitizers, but due to mutations caused by the browser are turned into dangerous code once processed after
|
||||
sanitization.
|
||||
@@ -1,16 +0,0 @@
|
||||
@ngdoc error
|
||||
@name linky:notstring
|
||||
@fullName Not a string
|
||||
@description
|
||||
|
||||
This error occurs when {@link ngSanitize.linky linky} is used with a non-empty, non-string value:
|
||||
```html
|
||||
<div ng-bind-html="42 | linky"></div>
|
||||
```
|
||||
|
||||
`linky` is supposed to be used with string values only, and therefore assumes that several methods
|
||||
(such as `.match()`) are available on the passed in value.
|
||||
The value can be initialized asynchronously and therefore null or undefined won't throw this error.
|
||||
|
||||
If you want to pass non-string values to `linky` (e.g. Objects whose `.toString()` should be
|
||||
utilized), you need to manually convert them to strings.
|
||||
@@ -1,52 +0,0 @@
|
||||
@ngdoc error
|
||||
@name orderBy:notarray
|
||||
@fullName Value is not array-like
|
||||
@description
|
||||
|
||||
This error occurs when {@link ng.orderBy orderBy} is not passed an array-like value:
|
||||
```html
|
||||
<div ng-repeat="(key, value) in myObj | orderBy:someProp">
|
||||
{{ key }} : {{ value }}
|
||||
</div>
|
||||
```
|
||||
|
||||
`orderBy` must be used with an array-like value so a subset of items can be returned.
|
||||
The array can be initialized asynchronously and therefore `null` or `undefined` won't throw this error.
|
||||
|
||||
To use `orderBy` to order the properties of an object, you can create your own array based on that object:
|
||||
```js
|
||||
angular.module('aModule', [])
|
||||
.controller('aController', function($scope) {
|
||||
var myObj = {
|
||||
one: {id: 1, name: 'Some thing'},
|
||||
two: {id: 2, name: 'Another thing'},
|
||||
three: {id: 3, name: 'A third thing'}
|
||||
};
|
||||
|
||||
$scope.arrFromMyObj = Object.keys(myObj).map(function(key) {
|
||||
return myObj[key];
|
||||
});
|
||||
});
|
||||
```
|
||||
That can be used as:
|
||||
```html
|
||||
<label>
|
||||
Order by:
|
||||
<select ng-model="orderProp" ng-options="prop for prop in ['id', 'name']"></select>
|
||||
</label>
|
||||
<div ng-repeat="item in arrFromMyObj | orderBy:orderProp">
|
||||
[{{ item.id }}] {{ item.name }}
|
||||
</div>
|
||||
```
|
||||
|
||||
You could as well convert the object to an array using a filter such as
|
||||
[toArrayFilter](https://github.com/petebacondarwin/angular-toArrayFilter):
|
||||
```html
|
||||
<label>
|
||||
Order by:
|
||||
<select ng-model="orderProp" ng-options="prop for prop in ['id', 'name']"></select>
|
||||
</label>
|
||||
<div ng-repeat="item in myObj | toArray:false | orderBy:orderProp">
|
||||
[{{ item.id }}] {{ item.name }}
|
||||
</div>
|
||||
```
|
||||
@@ -350,7 +350,7 @@ base and the path that should be handled by the application.
|
||||
### 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 consequencies...
|
||||
as the base href. This can have subtle confusing consequences...
|
||||
|
||||
Consider a base href set as follows: `<base href="/base/">` (i.e. the application exists in the "folder"
|
||||
called `/base`). The URL `/base` is actually outside the application (it refers to the `base` file found
|
||||
|
||||
@@ -33,9 +33,6 @@ Currently, ngAria interfaces with the following directives:
|
||||
|
||||
* {@link guide/accessibility#ngmodel ngModel}
|
||||
* {@link guide/accessibility#ngdisabled ngDisabled}
|
||||
* {@link guide/accessibility#ngrequired ngRequired}
|
||||
* {@link guide/accessibility#ngvaluechecked ngChecked}
|
||||
* {@link guide/accessibility#ngvaluechecked ngValue}
|
||||
* {@link guide/accessibility#ngshow ngShow}
|
||||
* {@link guide/accessibility#nghide ngHide}
|
||||
* {@link guide/accessibility#ngclick ngClick}
|
||||
@@ -44,7 +41,7 @@ Currently, ngAria interfaces with the following directives:
|
||||
|
||||
<h2 id="ngmodel">ngModel</h2>
|
||||
|
||||
Much of ngAria's heavy lifting happens in the {@link ng.ngModel ngModel}
|
||||
Much of ngAria's heavy lifting happens in the {@link ng.directive:ngModel ngModel}
|
||||
directive. For elements using ngModel, special attention is paid by ngAria if that element also
|
||||
has a role or type of `checkbox`, `radio`, `range` or `textbox`.
|
||||
|
||||
@@ -140,72 +137,32 @@ the keyboard. It is still up to **you** as a developer to **ensure custom contro
|
||||
accessible**. As a rule, any time you create a widget involving user interaction, be sure to test
|
||||
it with your keyboard and at least one mobile and desktop screen reader.
|
||||
|
||||
<h2 id="ngvaluechecked">ngValue and ngChecked</h2>
|
||||
|
||||
To ease the transition between native inputs and custom controls, ngAria now supports
|
||||
{@link ng.ngValue ngValue} and {@link ng.ngChecked ngChecked}.
|
||||
The original directives were created for native inputs only, so ngAria extends
|
||||
support to custom elements by managing `aria-checked` for accessibility.
|
||||
|
||||
###Example
|
||||
|
||||
```html
|
||||
<custom-checkbox ng-checked="val"></custom-checkbox>
|
||||
<custom-radio-button ng-value="val"></custom-radio-button>
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```html
|
||||
<custom-checkbox ng-checked="val" aria-checked="true"></custom-checkbox>
|
||||
<custom-radio-button ng-value="val" aria-checked="true"></custom-radio-button>
|
||||
```
|
||||
|
||||
<h2 id="ngdisabled">ngDisabled</h2>
|
||||
|
||||
The `disabled` attribute is only valid for certain elements such as `button`, `input` and
|
||||
`textarea`. To properly disable custom element directives such as `<md-checkbox>` or `<taco-tab>`,
|
||||
using ngAria with {@link ng.ngDisabled ngDisabled} will also
|
||||
using ngAria with {@link ng.directive:ngDisabled ngDisabled} will also
|
||||
add `aria-disabled`. This tells assistive technologies when a non-native input is disabled, helping
|
||||
custom controls to be more accessible.
|
||||
|
||||
###Example
|
||||
|
||||
```html
|
||||
<md-checkbox ng-disabled="disabled"></md-checkbox>
|
||||
<md-checkbox ng-disabled="disabled">
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```html
|
||||
<md-checkbox disabled aria-disabled="true"></md-checkbox>
|
||||
<md-checkbox disabled aria-disabled="true">
|
||||
```
|
||||
|
||||
>You can check whether a control is legitimately disabled for a screen reader by visiting
|
||||
[chrome://accessibility](chrome://accessibility) and inspecting [the accessibility tree](http://www.paciellogroup.com/blog/2015/01/the-browser-accessibility-tree/).
|
||||
|
||||
<h2 id="ngrequired">ngRequired</h2>
|
||||
|
||||
The boolean `required` attribute is only valid for native form controls such as `input` and
|
||||
`textarea`. To properly indicate custom element directives such as `<md-checkbox>` or `<custom-input>`
|
||||
as required, using ngAria with {@link ng.ngRequired ngRequired} will also add
|
||||
`aria-required`. This tells accessibility APIs when a custom control is required.
|
||||
|
||||
###Example
|
||||
|
||||
```html
|
||||
<md-checkbox ng-required="val"></md-checkbox>
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```html
|
||||
<md-checkbox ng-required="val" aria-required="true"></md-checkbox>
|
||||
```
|
||||
|
||||
<h2 id="ngshow">ngShow</h2>
|
||||
|
||||
>The {@link ng.ngShow ngShow} directive shows or hides the
|
||||
>The {@link ng.directive:ngShow ngShow} directive shows or hides the
|
||||
given HTML element based on the expression provided to the `ngShow` attribute. The element is
|
||||
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
|
||||
|
||||
@@ -242,7 +199,7 @@ Becomes:
|
||||
|
||||
<h2 id="nghide">ngHide</h2>
|
||||
|
||||
>The {@link ng.ngHide ngHide} directive shows or hides the
|
||||
>The {@link ng.directive:ngHide ngHide} directive shows or hides the
|
||||
given HTML element based on the expression provided to the `ngHide` attribute. The element is
|
||||
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
# Animations
|
||||
|
||||
AngularJS provides animation hooks for common directives such as `ngRepeat`, `ngSwitch`, and `ngView`, as well as custom directives
|
||||
AngularJS 1.3 provides animation hooks for common directives such as `ngRepeat`, `ngSwitch`, and `ngView`, as well as custom directives
|
||||
via the `$animate` service. These animation hooks are set in place to trigger animations during the life cycle of various directives and when
|
||||
triggered, will attempt to perform a CSS Transition, CSS Keyframe Animation or a JavaScript callback Animation (depending on if an animation is
|
||||
placed on the given directive). Animations can be placed using vanilla CSS by following the naming conventions set in place by AngularJS
|
||||
@@ -274,100 +274,6 @@ myModule.directive('my-directive', ['$animate', function($animate) {
|
||||
}]);
|
||||
```
|
||||
|
||||
## Animations on app bootstrap / page load
|
||||
|
||||
By default, animations are disabled when the Angular app {@link guide/bootstrap bootstraps}. If you are using the {@link ngApp} directive,
|
||||
this happens in the `DOMContentLoaded` event, so immediately after the page has been loaded.
|
||||
Animations are disabled, so that UI and content are instantly visible. Otherwise, with many animations on
|
||||
the page, the loading process may become too visually overwhelming, and the performance may suffer.
|
||||
|
||||
Internally, `ngAnimate` waits until all template downloads that are started right after bootstrap have finished.
|
||||
Then, it waits for the currently running {@link ng.$rootScope.Scope#$digest} and the one after that to finish.
|
||||
This ensures that the whole app has been compiled fully before animations are attempted.
|
||||
|
||||
If you do want your animations to play when the app bootstraps, you can enable animations globally in
|
||||
your main module's {@link angular.Module#run run} function:
|
||||
|
||||
```js
|
||||
myModule.run(function($animate) {
|
||||
$animate.enabled(true);
|
||||
});
|
||||
```
|
||||
|
||||
## How to (selectively) enable, disable and skip animations
|
||||
|
||||
There are three different ways to disable animations, both globally and for specific animations.
|
||||
Disabling specific animations can help to speed up the render performance, for example for large `ngRepeat`
|
||||
lists that don't actually have animations. Because ngAnimate checks at runtime if animations are present,
|
||||
performance will take a hit even if an element has no animation.
|
||||
|
||||
### In the config: {@link $animateProvider#classNameFilter $animateProvider.classNameFilter()}
|
||||
|
||||
This function can be called in the {@link angular.Module#config config} phase of an app. It takes a regex as the only argument,
|
||||
which will then be matched against the classes of any element that is about to be animated. The regex
|
||||
allows a lot of flexibility - you can either allow animations only for specific classes (useful when
|
||||
you are working with 3rd party animations), or exclude specific classes from getting animated.
|
||||
|
||||
```js
|
||||
app.config(function($animateProvider) {
|
||||
$animateProvider.classNameFilter(/animate-/);
|
||||
});
|
||||
```
|
||||
|
||||
```css
|
||||
/* prefixed with animate- */
|
||||
.animate-fade-add.animate-fade-add-active {
|
||||
transition:1s linear all;
|
||||
opacity:0;
|
||||
}
|
||||
```
|
||||
|
||||
The classNameFilter approach generally applies the biggest speed boost, because the matching is
|
||||
done before any other animation disabling strategies are checked. However, that also means it is not
|
||||
possible to override class name matching with the two following strategies. It's of course still possible
|
||||
to enable / disable animations by changing an element's class name at runtime.
|
||||
|
||||
### At runtime: {@link ng.$animate#enabled $animate.enabled()}
|
||||
|
||||
This function can be used to enable / disable animations in two different ways:
|
||||
|
||||
With a single `boolean` argument, it enables / disables animations globally: `$animate.enabled(false)`
|
||||
disables all animations in your app.
|
||||
|
||||
When the second argument is a native DOM or jQuery element, the function enables / disables
|
||||
animations on this element *and all its children*: `$animate.enabled(false, myElement)`. This is the
|
||||
most flexible way to change the animation state. For example, even if you have used it to disable
|
||||
animations on a parent element, you can still re-enable it for a child element. And compared to the
|
||||
`classNameFilter`, you can change the animation status at runtime instead of during the config phase.
|
||||
|
||||
Note however that the `$animate.enabled()` state for individual elements does not overwrite disabling
|
||||
rules that have been set in the {@link $animateProvider#classNameFilter classNameFilter}.
|
||||
|
||||
### Via CSS styles: overwriting styles in the `ng-animate` CSS class
|
||||
Whenever an animation is started, ngAnimate applies the `ng-animate` class to the element for the
|
||||
whole duration of the animation. By applying CSS transition / animation styling to the class,
|
||||
you can skip an animation:
|
||||
|
||||
```css
|
||||
|
||||
.my-class{
|
||||
transition: transform 2s;
|
||||
}
|
||||
|
||||
.my-class:hover {
|
||||
transform: translateX(50px);
|
||||
}
|
||||
|
||||
my-class.ng-animate {
|
||||
transition: 0s;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
By setting `transition: 0s`, ngAnimate will ignore the existing transition styles, and not try to animate them (Javascript
|
||||
animations will still execute, though). This can be used to prevent {@link guide/animations#preventing-collisions-with-existing-animations-and-third-party-libraries
|
||||
issues with existing animations interfering with ngAnimate}.
|
||||
|
||||
## Preventing flicker before an animation starts
|
||||
|
||||
When nesting elements with structural animations such as `ngIf` into elements that have class-based
|
||||
@@ -399,49 +305,6 @@ In that case, you can add styles to the CSS that make sure the element stays hid
|
||||
/* Other animation styles ... */
|
||||
```
|
||||
|
||||
## Preventing Collisions with Existing Animations and Third Party Libraries
|
||||
By default, any `ngAnimate` enabled directives will assume any transition / animation styles on the
|
||||
element are part of an `ngAnimate` animation. This can lead to problems when the styles are actually
|
||||
for animations that are independent of `ngAnimate`.
|
||||
|
||||
For example, an element acts as a loading spinner. It has an inifinite css animation on it, and also an
|
||||
{@link ngIf `ngIf`} directive, for which no animations are defined:
|
||||
|
||||
```css
|
||||
@keyframes rotating {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.spinner {
|
||||
animation: rotating 2s linear infinite;
|
||||
}
|
||||
```
|
||||
|
||||
Now, when the `ngIf` changes, `ngAnimate` will see the spinner animation and use
|
||||
it to animate the `enter`/`leave` event, which doesn't work because
|
||||
the animation is infinite. The element will still be added / removed after a timeout, but there will be a
|
||||
noticable delay.
|
||||
|
||||
This might also happen because some third-party frameworks place animation duration defaults
|
||||
across many element or className selectors in order to make their code small and reuseable.
|
||||
|
||||
You can prevent this unwanted behavior by adding CSS to the `.ng-animate` class that is added
|
||||
for the whole duration of an animation. Simply overwrite the transition / animation duration. In the
|
||||
case of the spinner, this would be:
|
||||
|
||||
```css
|
||||
.spinner.ng-animate {
|
||||
transition: 0s none;
|
||||
animation: 0s none;
|
||||
}
|
||||
```
|
||||
|
||||
If you do have CSS transitions / animations defined for the animation events, make sure they have higher priority
|
||||
than any styles that are independent from ngAnimate.
|
||||
|
||||
You can also use one of the two other {@link guide/animations#how-to-selectively-enable-disable-and-skip-animations strategies to disable animations}.
|
||||
|
||||
## More about animations
|
||||
|
||||
For a full breakdown of each method available on `$animate`, see the {@link ng.$animate API documentation}.
|
||||
|
||||
@@ -53,13 +53,13 @@ initialization.
|
||||
|
||||
Angular initializes automatically upon `DOMContentLoaded` event or when the `angular.js` script is
|
||||
evaluated if at that time `document.readyState` is set to `'complete'`. At this point Angular looks
|
||||
for the {@link ng.directive:ngApp `ngApp`} directive which designates your application root.
|
||||
If the {@link ng.directive:ngApp `ngApp`} directive is found then Angular will:
|
||||
for the {@link ng.directive:ngApp `ng-app`} directive which designates your application root.
|
||||
If the {@link ng.directive:ngApp `ng-app`} directive is found then Angular will:
|
||||
|
||||
* load the {@link guide/module module} associated with the directive.
|
||||
* create the application {@link auto.$injector injector}
|
||||
* compile the DOM treating the {@link ng.directive:ngApp
|
||||
`ngApp`} directive as the root of the compilation. This allows you to tell it to treat only a
|
||||
`ng-app`} directive as the root of the compilation. This allows you to tell it to treat only a
|
||||
portion of the DOM as an Angular application.
|
||||
|
||||
|
||||
@@ -142,17 +142,6 @@ This is the sequence that your code should follow:
|
||||
2. Call {@link angular.bootstrap} to {@link compiler compile} the element into an
|
||||
executable, bi-directionally bound application.
|
||||
|
||||
## Things to keep in mind
|
||||
|
||||
There a few things to keep in mind regardless of automatic or manual bootstrapping:
|
||||
|
||||
- While it's possible to bootstrap more than one AngularJS application per page, we don't actively
|
||||
test against this scenario. It's possible that you'll run into problems, especially with complex apps, so
|
||||
caution is advised.
|
||||
- Do not bootstrap your app on an element with a directive that uses {@link ng.$compile#transclusion transclusion}, such as
|
||||
{@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
|
||||
Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
|
||||
causing animations to stop working and making the injector inaccessible from outside the app.
|
||||
|
||||
## Deferred Bootstrap
|
||||
|
||||
|
||||
@@ -1,471 +0,0 @@
|
||||
@ngdoc overview
|
||||
@name Components
|
||||
@sortOrder 305
|
||||
@description
|
||||
|
||||
# Understanding Components
|
||||
|
||||
In Angular, a Component is a special kind of {@link guide/directive directive} that uses a simpler
|
||||
configuration which is suitable for a component-based application structure.
|
||||
|
||||
This makes it easier to write an app in a way that's similar to using Web Components or using Angular
|
||||
2's style of application architecture.
|
||||
|
||||
Advantages of Components:
|
||||
- simpler configuration than plain directives
|
||||
- promote sane defaults and best practices
|
||||
- optimized for component-based architecture
|
||||
- writing component directives will make it easier to upgrade to Angular 2
|
||||
|
||||
When not to use Components:
|
||||
|
||||
- for directives that rely on DOM manipulation, adding event listeners etc, because the compile
|
||||
and link functions are unavailable
|
||||
- when you need advanced directive definition options like priority, terminal, multi-element
|
||||
- when you want a directive that is triggered by an attribute or CSS class, rather than an element
|
||||
|
||||
## Creating and configuring a Component
|
||||
|
||||
Components can be registered using the `.component()` method of an Angular 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.
|
||||
|
||||
<example name="heroComponentSimple" module="heroApp">
|
||||
<file name="index.js">
|
||||
angular.module('heroApp', []).controller('mainCtrl', function() {
|
||||
this.hero = {
|
||||
name: 'Spawn'
|
||||
};
|
||||
});
|
||||
</file>
|
||||
<file name="heroDetail.js">
|
||||
|
||||
function HeroDetailController() {
|
||||
|
||||
}
|
||||
|
||||
angular.module('heroApp').component('heroDetail', {
|
||||
templateUrl: 'heroDetail.html',
|
||||
controller: HeroDetailController,
|
||||
bindings: {
|
||||
hero: '='
|
||||
}
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<!-- components match only elements -->
|
||||
<div ng-controller="mainCtrl as ctrl">
|
||||
<b>Hero</b><br>
|
||||
<hero-detail hero="ctrl.hero"></hero-detail>
|
||||
</div>
|
||||
</file>
|
||||
<file name="heroDetail.html">
|
||||
<span>Name: {{$ctrl.hero.name}}</span>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
It's also possible to add components via {@link $compileProvider#component} in a module's config phase.
|
||||
|
||||
### Comparison between Directive definition and Component definition
|
||||
|
||||
| | Directive | Component |
|
||||
|-------------------|----------------------|-----------------|
|
||||
| bindings | No | Yes (binds to controller) |
|
||||
| bindToController | Yes (default: false) | No (use bindings instead) |
|
||||
| compile function | Yes | No |
|
||||
| controller | Yes | Yes (default `function() {}`) |
|
||||
| controllerAs | Yes (default: false) | Yes (default: `$ctrl`) |
|
||||
| link functions | Yes | No |
|
||||
| multiElement | Yes | No |
|
||||
| priority | Yes | No |
|
||||
| require | Yes | Yes |
|
||||
| restrict | Yes | No (restricted to elements only) |
|
||||
| scope | Yes (default: false) | No (scope is always isolate) |
|
||||
| template | Yes | Yes, injectable |
|
||||
| templateNamespace | Yes | No |
|
||||
| templateUrl | Yes | Yes, injectable |
|
||||
| terminal | Yes | No |
|
||||
| transclude | Yes (default: false) | Yes (default: false) |
|
||||
|
||||
|
||||
## Component-based application architecture
|
||||
|
||||
As already mentioned, the component helper makes it easier to structure your application with
|
||||
a component-based architecture. But what makes a component beyond the options that
|
||||
the component helper has?
|
||||
|
||||
- **Components only control their own View and Data:**
|
||||
Components should never modify any data or DOM that is out of their own scope. Normally, in Angular
|
||||
it is possible to modify data anywhere in the application through scope inheritance and watches. This
|
||||
is practical, but can also lead to problems when it is not clear which part of the application is
|
||||
responsible for modifying the data. That is why component directives use an isolate scope, so a whole
|
||||
class of scope manipulation is not possible.
|
||||
|
||||
- **Components have a well-defined public API - Inputs and Outputs:**
|
||||
However, scope isolation only goes so far, because Angular uses two-way binding. So if you pass
|
||||
an object to a component like this - `bindings: {item: '='}`, and modify one of its properties, the
|
||||
change will be reflected in the parent component. For components however, only the component that owns
|
||||
the data should modify it, to make it easy to reason about what data is changed, and when. For that reason,
|
||||
components should follow a few simple conventions:
|
||||
|
||||
- Inputs should be using `<` and `@` bindings. The `<` symbol denotes {@link $compile#-scope- one-way bindings} which are
|
||||
available since 1.5. The difference to `=` is that the bound properties in the component scope are not watched, which means
|
||||
if you assign a new value to the property in the component scope, it will not update the parent scope. Note however, that both parent
|
||||
and component scope reference the same object, so if you are changing object properties or array elements in the
|
||||
component, the parent will still reflect that change.
|
||||
The general rule should therefore be to never change an object or array property in the component scope.
|
||||
`@` bindings can be used when the input is a string, especially when the value of the binding doesn't change.
|
||||
```js
|
||||
bindings: {
|
||||
hero: '<',
|
||||
comment: '@'
|
||||
}
|
||||
```
|
||||
- Outputs are realized with `&` bindings, which function as callbacks to component events.
|
||||
```js
|
||||
bindings: {
|
||||
onDelete: '&',
|
||||
onUpdate: '&'
|
||||
}
|
||||
```
|
||||
- Instead of manipulating Input Data, the component calls the correct Output Event with the changed data.
|
||||
For a deletion, that means the component doesn't delete the `hero` itself, but sends it back to
|
||||
the owner component via the correct event.
|
||||
```html
|
||||
<button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
|
||||
```
|
||||
- That way, the parent component can decide what to do with the event (e.g. delete an item or update the properties)
|
||||
```js
|
||||
ctrl.deleteHero(hero) {
|
||||
$http.delete(...).then(function() {
|
||||
var idx = ctrl.list.indexOf(hero);
|
||||
if (idx >= 0) {
|
||||
ctrl.list.splice(idx, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
- **An application is a tree of components:**
|
||||
Ideally, the whole application should be a tree of components that implement clearly defined inputs
|
||||
and outputs, and minimize two-way data binding. That way, it's easier to predict when data changes and what the state
|
||||
of a component is.
|
||||
|
||||
## Example of a component tree
|
||||
|
||||
The following example expands on the simple component example and incorporates the concepts we introduced
|
||||
above:
|
||||
|
||||
Instead of an ngController, we now have a heroList component that holds the data of
|
||||
different heroes, and creates a heroDetail for each of them.
|
||||
|
||||
The heroDetail component now contains new functionality:
|
||||
- a delete button that calls the bound `onDelete` function of the heroList component
|
||||
- an input to change the hero location, in the form of a reusable editableField component. Instead
|
||||
of manipulating the hero object itself, it sends a changeset upwards to the heroDetail, which sends
|
||||
it upwards to the heroList component, which updates the original data.
|
||||
|
||||
<example name="heroComponentTree" module="heroApp">
|
||||
<file name="index.js">
|
||||
var mode = angular.module('heroApp', []);
|
||||
</file>
|
||||
|
||||
<file name="heroList.js">
|
||||
function HeroListController($scope, $element, $attrs) {
|
||||
var ctrl = this;
|
||||
|
||||
// This would be loaded by $http etc.
|
||||
ctrl.list = [
|
||||
{
|
||||
name: 'Superman',
|
||||
location: ''
|
||||
},
|
||||
{
|
||||
name: 'Batman',
|
||||
location: 'Wayne Manor'
|
||||
}
|
||||
];
|
||||
|
||||
ctrl.updateHero = function(hero, prop, value) {
|
||||
hero[prop] = value;
|
||||
};
|
||||
|
||||
ctrl.deleteHero = function(hero) {
|
||||
var idx = ctrl.list.indexOf(hero);
|
||||
if (idx >= 0) {
|
||||
ctrl.list.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('heroApp').component('heroList', {
|
||||
templateUrl: 'heroList.html',
|
||||
controller: HeroListController
|
||||
});
|
||||
|
||||
</file>
|
||||
|
||||
<file name="heroDetail.js">
|
||||
function HeroDetailController($scope, $element, $attrs) {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.update = function(prop, value) {
|
||||
ctrl.onUpdate({hero: ctrl.hero, prop: prop, value: value});
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('heroApp').component('heroDetail', {
|
||||
templateUrl: 'heroDetail.html',
|
||||
controller: HeroDetailController,
|
||||
bindings: {
|
||||
hero: '<',
|
||||
onDelete: '&',
|
||||
onUpdate: '&'
|
||||
}
|
||||
});
|
||||
</file>
|
||||
|
||||
<file name="editableField.js">
|
||||
|
||||
function EditableFieldController($scope, $element, $attrs) {
|
||||
var ctrl = this;
|
||||
ctrl.editMode = false;
|
||||
|
||||
ctrl.handleModeChange = function() {
|
||||
if (ctrl.editMode) {
|
||||
ctrl.onUpdate({value: ctrl.fieldValue});
|
||||
ctrl.fieldValueCopy = ctrl.fieldValue;
|
||||
}
|
||||
ctrl.editMode = !ctrl.editMode;
|
||||
};
|
||||
|
||||
ctrl.reset = function() {
|
||||
ctrl.fieldValue = ctrl.fieldValueCopy;
|
||||
};
|
||||
|
||||
ctrl.$onInit = function() {
|
||||
// Make a copy of the initial value to be able to reset it later
|
||||
ctrl.fieldValueCopy = ctrl.fieldValue;
|
||||
|
||||
// Set a default fieldType
|
||||
if (!ctrl.fieldType) {
|
||||
ctrl.fieldType = 'text';
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('heroApp').component('editableField', {
|
||||
templateUrl: 'editableField.html',
|
||||
controller: EditableFieldController,
|
||||
bindings: {
|
||||
fieldValue: '<',
|
||||
fieldType: '@?',
|
||||
onUpdate: '&'
|
||||
}
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<hero-list></hero-list>
|
||||
</file>
|
||||
<file name="heroList.html">
|
||||
<b>Heroes</b><br>
|
||||
<hero-detail ng-repeat="hero in $ctrl.list" hero="hero" on-delete="$ctrl.deleteHero(hero)" on-update="$ctrl.updateHero(hero, prop, value)"></hero-detail>
|
||||
</file>
|
||||
<file name="heroDetail.html">
|
||||
<hr>
|
||||
<div>
|
||||
Name: {{$ctrl.hero.name}}<br>
|
||||
Location: <editable-field field-value="$ctrl.hero.location" field-type="text" on-update="$ctrl.update('location', value)"></editable-field><br>
|
||||
<button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
|
||||
</div>
|
||||
</file>
|
||||
<file name="editableField.html">
|
||||
<span ng-switch="$ctrl.editMode">
|
||||
<input ng-switch-when="true" type="{{$ctrl.fieldType}}" ng-model="$ctrl.fieldValue">
|
||||
<span ng-switch-default>{{$ctrl.fieldValue}}</span>
|
||||
</span>
|
||||
<button ng-click="$ctrl.handleModeChange()">{{$ctrl.editMode ? 'Save' : 'Edit'}}</button>
|
||||
<button ng-if="$ctrl.editMode" ng-click="$ctrl.reset()">Reset</button>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
## Components as route templates
|
||||
Components are also useful as route templates (e.g. when using {@link ngRoute ngRoute}). In a component-based
|
||||
application, every view is a component:
|
||||
|
||||
```js
|
||||
var myMod = angular.module('myMod', ['ngRoute']);
|
||||
myMod.component('home', {
|
||||
template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
|
||||
controller: function() {
|
||||
this.user = {name: 'world'};
|
||||
}
|
||||
});
|
||||
myMod.config(function($routeProvider) {
|
||||
$routeProvider.when('/', {
|
||||
template: '<home></home>'
|
||||
});
|
||||
});
|
||||
```
|
||||
<br />
|
||||
When using {@link ngRoute.$routeProvider $routeProvider}, you can often avoid some
|
||||
boilerplate, by passing the resolved route dependencies directly to the component. Since 1.5,
|
||||
ngRoute automatically assigns the resolves to the route scope property `$resolve` (you can also
|
||||
configure the property name via `resolveAs`). When using components, you can take advantage of this and pass resolves
|
||||
directly into your component without creating an extra route controller:
|
||||
|
||||
```js
|
||||
var myMod = angular.module('myMod', ['ngRoute']);
|
||||
myMod.component('home', {
|
||||
template: '<h1>Home</h1><p>Hello, {{ $ctrl.user.name }} !</p>',
|
||||
bindings: {
|
||||
user: '<'
|
||||
}
|
||||
});
|
||||
myMod.config(function($routeProvider) {
|
||||
$routeProvider.when('/', {
|
||||
template: '<home user="$resolve.user"></home>',
|
||||
resolve: {
|
||||
user: function($http) { return $http.get('...'); }
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Intercomponent Communication
|
||||
|
||||
Directives can require the controllers of other directives to enable communication
|
||||
between each other. This can be achieved in a component by providing an
|
||||
object mapping for the `require` property. The object keys specify the property names under which
|
||||
the required controllers (object values) will be bound to the requiring component's controller.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
Note that the required controllers will not be available during the instantiation of the controller,
|
||||
but they are guaranteed to be available just before the `$onInit` method is executed!
|
||||
</div>
|
||||
|
||||
Here is a tab pane example built from components:
|
||||
|
||||
<example module="docsTabsExample">
|
||||
<file name="script.js">
|
||||
angular.module('docsTabsExample', [])
|
||||
.component('myTabs', {
|
||||
transclude: true,
|
||||
controller: function() {
|
||||
var panes = this.panes = [];
|
||||
this.select = function(pane) {
|
||||
angular.forEach(panes, function(pane) {
|
||||
pane.selected = false;
|
||||
});
|
||||
pane.selected = true;
|
||||
};
|
||||
this.addPane = function(pane) {
|
||||
if (panes.length === 0) {
|
||||
this.select(pane);
|
||||
}
|
||||
panes.push(pane);
|
||||
};
|
||||
},
|
||||
templateUrl: 'my-tabs.html'
|
||||
})
|
||||
.component('myPane', {
|
||||
transclude: true,
|
||||
require: {
|
||||
tabsCtrl: '^myTabs'
|
||||
},
|
||||
bindings: {
|
||||
title: '@'
|
||||
},
|
||||
controller: function() {
|
||||
this.$onInit = function() {
|
||||
this.tabsCtrl.addPane(this);
|
||||
console.log(this);
|
||||
};
|
||||
},
|
||||
templateUrl: 'my-pane.html'
|
||||
});
|
||||
</file>
|
||||
<file name="index.html">
|
||||
<my-tabs>
|
||||
<my-pane title="Hello">
|
||||
<h4>Hello</h4>
|
||||
<p>Lorem ipsum dolor sit amet</p>
|
||||
</my-pane>
|
||||
<my-pane title="World">
|
||||
<h4>World</h4>
|
||||
<em>Mauris elementum elementum enim at suscipit.</em>
|
||||
<p><a href ng-click="i = i + 1">counter: {{i || 0}}</a></p>
|
||||
</my-pane>
|
||||
</my-tabs>
|
||||
</file>
|
||||
<file name="my-tabs.html">
|
||||
<div class="tabbable">
|
||||
<ul class="nav nav-tabs">
|
||||
<li ng-repeat="pane in $ctrl.panes" ng-class="{active:pane.selected}">
|
||||
<a href="" ng-click="$ctrl.select(pane)">{{pane.title}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" ng-transclude></div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="my-pane.html">
|
||||
<div class="tab-pane" ng-show="$ctrl.selected" ng-transclude></div>
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
# Unit-testing Component Controllers
|
||||
|
||||
The easiest way to unit-test a component controller is by using the {@link ngMock.$componentController $componentController}
|
||||
that is included in {@link ngMock}. The advantage of this method is that you do not have
|
||||
to create any DOM elements. The following example shows how to do this for the `heroDetail` component
|
||||
from above.
|
||||
|
||||
The examples use the [Jasmine](http://jasmine.github.io/) testing framework.
|
||||
|
||||
**Controller Test:**
|
||||
```js
|
||||
describe('component: heroDetail', function() {
|
||||
var component, scope, hero, $componentController;
|
||||
|
||||
beforeEach(module('simpleComponent'));
|
||||
|
||||
beforeEach(inject(function($rootScope, _$componentController_) {
|
||||
scope = $rootScope.$new();
|
||||
$componentController = _$componentController_;
|
||||
hero = {name: 'Wolverine'};
|
||||
}));
|
||||
|
||||
it('should set the default values of the hero', function() {
|
||||
// It's necessary to always pass the scope in the locals, so that the controller instance can be bound to it
|
||||
component = $componentController('heroDetail', {$scope: scope});
|
||||
|
||||
expect(component.hero).toEqual({
|
||||
name: undefined,
|
||||
location: 'unknown'
|
||||
});
|
||||
});
|
||||
|
||||
it('should assign the name bindings to the hero object', function() {
|
||||
// Here we are passing actual bindings to the component
|
||||
|
||||
component = $componentController('heroDetail',
|
||||
{$scope: scope},
|
||||
{hero: hero}
|
||||
);
|
||||
expect(component.hero.name).toBe('Wolverine');
|
||||
});
|
||||
|
||||
it('should call the onDelete binding when a hero is deleted', function() {
|
||||
component = $componentController('heroDetail',
|
||||
{$scope: scope},
|
||||
{hero: hero, onDelete: jasmine.createSpy('deleteSpy')}
|
||||
);
|
||||
|
||||
component.onDelete({hero: component.hero});
|
||||
expect(spy('deleteSpy')).toHaveBeenCalledWith(component.hero);
|
||||
});
|
||||
|
||||
});
|
||||
```
|
||||
@@ -334,9 +334,9 @@ The following example shows how this is done with Angular:
|
||||
var refresh = function() {
|
||||
var url = YAHOO_FINANCE_URL_PATTERN.
|
||||
replace('PAIRS', 'USD' + currencies.join('","USD'));
|
||||
return $http.jsonp(url).then(function(response) {
|
||||
return $http.jsonp(url).success(function(data) {
|
||||
var newUsdToForeignRates = {};
|
||||
angular.forEach(response.data.query.results.rate, function(rate) {
|
||||
angular.forEach(data.query.results.rate, function(rate) {
|
||||
var currency = rate.id.substring(3,6);
|
||||
newUsdToForeignRates[currency] = window.parseFloat(rate.Rate);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,486 @@
|
||||
@ngdoc overview
|
||||
@name Decorators
|
||||
@sortOrder 345
|
||||
@description
|
||||
|
||||
# Decorators in AngularJS
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**NOTE:** This guide is targeted towards developers who are already familiar with AngularJS basics.
|
||||
If you're just getting started, we recommend the {@link tutorial/ tutorial} first.
|
||||
</div>
|
||||
|
||||
## What are decorators?
|
||||
|
||||
Decorators are a design pattern that is used to separate modification or *decoration* of a class without modifying the
|
||||
original source code. In Angular, decorators are functions that allow a service, directive or filter to be modified
|
||||
prior to its usage.
|
||||
|
||||
## How to use decorators
|
||||
|
||||
There are two ways to register decorators
|
||||
|
||||
- `$provide.decorator`, and
|
||||
- `module.decorator`
|
||||
|
||||
Each provide access to a `$delegate`, which is the instantiated service/directive/filter, prior to being passed to the
|
||||
service that required it.
|
||||
|
||||
### $provide.decorator
|
||||
|
||||
The {@link api/auto/service/$provide#decorator decorator function} allows access to a $delegate of the service once it
|
||||
has been instantiated. For example:
|
||||
|
||||
```js
|
||||
angular.module('myApp', [])
|
||||
|
||||
.config([ '$provide', function($provide) {
|
||||
|
||||
$provide.decorator('$log', [
|
||||
'$delegate',
|
||||
function $logDecorator($delegate) {
|
||||
|
||||
var originalWarn = $delegate.warn;
|
||||
$delegate.warn = function decoratedWarn(msg) {
|
||||
msg = 'Decorated Warn: ' + msg;
|
||||
originalWarn.apply($delegate, arguments);
|
||||
};
|
||||
|
||||
return $delegate;
|
||||
}
|
||||
]);
|
||||
}]);
|
||||
```
|
||||
|
||||
After the `$log` service has been instantiated the decorator is fired. The decorator function has a `$delegate` object
|
||||
injected to provide access to the service that matches the selector in the decorator. This `$delegate` will be the
|
||||
service you are decorating. The return value of the function *provided to the decorator* will take place of the service,
|
||||
directive, or filter being decorated.
|
||||
|
||||
<hr>
|
||||
|
||||
The `$delegate` may be either modified or completely replaced. Given a service `myService` with a method `someFn`, the
|
||||
following could all be viable solutions:
|
||||
|
||||
|
||||
#### Completely Replace the $delegate
|
||||
```js
|
||||
angular.module('myApp', [])
|
||||
|
||||
.config([ '$provide', function($provide) {
|
||||
|
||||
$provide.decorator('myService', [
|
||||
'$delegate',
|
||||
function myServiceDecorator($delegate) {
|
||||
|
||||
var myDecoratedService = {
|
||||
// new service object to replace myService
|
||||
};
|
||||
return myDecoratedService;
|
||||
}
|
||||
]);
|
||||
}]);
|
||||
```
|
||||
|
||||
#### Patch the $delegate
|
||||
```js
|
||||
angular.module('myApp', [])
|
||||
|
||||
.config([ '$provide', function($provide) {
|
||||
|
||||
$provide.decorator('myService', [
|
||||
'$delegate',
|
||||
function myServiceDecorator($delegate) {
|
||||
|
||||
var someFn = $delegate.someFn;
|
||||
|
||||
function aNewFn() {
|
||||
// new service function
|
||||
someFn.apply($delegate, arguments);
|
||||
}
|
||||
|
||||
$delegate.someFn = aNewFn;
|
||||
return $delegate;
|
||||
}
|
||||
]);
|
||||
}]);
|
||||
```
|
||||
|
||||
#### Augment the $delegate
|
||||
```js
|
||||
angular.module('myApp', [])
|
||||
|
||||
.config([ '$provide', function($provide) {
|
||||
|
||||
$provide.decorator('myService', [
|
||||
'$delegate',
|
||||
function myServiceDecorator($delegate) {
|
||||
|
||||
function helperFn() {
|
||||
// an additional fn to add to the service
|
||||
}
|
||||
|
||||
$delegate.aHelpfulAddition = helperFn;
|
||||
return $delegate;
|
||||
}
|
||||
]);
|
||||
}]);
|
||||
```
|
||||
|
||||
<div class="alert alert-info">
|
||||
Note that whatever is returned by the decorator function will replace that which is being decorated. For example, a
|
||||
missing return statement will wipe out the entire object being decorated.
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
Decorators have different rules for different services. This is because services are registered in different ways.
|
||||
Services are selected by name, however filters and directives are selected by appending `"Filter"` or `"Directive"` to
|
||||
the end of the name. The `$delegate` provided is dictated by the type of service.
|
||||
|
||||
| Service Type | Selector | $delegate |
|
||||
|--------------|-------------------------------|-----------------------------------------------------------------------|
|
||||
| Service | `serviceName` | The `object` or `function` returned by the service |
|
||||
| Directive | `directiveName + 'Directive'` | An `Array.<DirectiveObject>`<sub>{@link guide/decorators#drtvArray 1}</sub> |
|
||||
| Filter | `filterName + 'Filter'` | The `function` returned by the filter |
|
||||
|
||||
<small id="drtvArray">1. Multiple directives may be registered to the same selector/name</small>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**NOTE:** Developers should take care in how and why they are modifying the `$delegate` for the service. Not only
|
||||
should expectations for the consumer be kept, but some functionality (such as directive registration) does not take
|
||||
place after decoration, but during creation/registration of the original service. This means, for example, that
|
||||
an action such as pushing a directive object to a directive `$delegate` will likely result in unexpected behavior.
|
||||
|
||||
Furthermore, great care should be taken when decorating core services, directives, or filters as this may unexpectedly
|
||||
or adversely affect the functionality of the framework.
|
||||
</div>
|
||||
|
||||
### module.decorator
|
||||
|
||||
This {@link api/ng/type/angular.Module#decorator function} is the same as the `$provide.decorator` function except it is
|
||||
exposed through the module API. This allows you to separate your decorator patterns from your module config blocks. The
|
||||
main caveat here is that you will need to take note the order in which you create your decorators.
|
||||
|
||||
Unlike in the module config block (which allows configuration of services prior to their creation), the service must be
|
||||
registered prior to the decorator (see {@link guide/providers#provider-recipe Provider Recipe}). For example, the
|
||||
following would not work because you are attempting to decorate outside of the configuration phase and the service
|
||||
hasn't been created yet:
|
||||
|
||||
```js
|
||||
// will cause an error since 'someService' hasn't been registered
|
||||
angular.module('myApp').decorator('someService', ...);
|
||||
|
||||
angular.module('myApp').factory('someService', ...);
|
||||
```
|
||||
|
||||
## Example Applications
|
||||
|
||||
The following sections provide examples each of a service decorator, a directive decorator, and a filter decorator.
|
||||
|
||||
### Service Decorator Example
|
||||
|
||||
This example shows how we can replace the $log service with our own to display log messages.
|
||||
|
||||
<example module="myServiceDecorator" name="service-decorator">
|
||||
<file name="script.js">
|
||||
angular.module('myServiceDecorator', []).
|
||||
|
||||
controller('Ctrl', [
|
||||
'$scope',
|
||||
'$log',
|
||||
'$timeout',
|
||||
function($scope, $log, $timeout) {
|
||||
var types = ['error', 'warn', 'log', 'info' ,'debug'], i;
|
||||
|
||||
for (i = 0; i < types.length; i++) {
|
||||
$log[types[i]](types[i] + ': message ' + (i + 1));
|
||||
}
|
||||
|
||||
$timeout(function() {
|
||||
$log.info('info: message logged in timeout');
|
||||
});
|
||||
}
|
||||
]).
|
||||
|
||||
directive('myLog', [
|
||||
'$log',
|
||||
function($log) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: '<ul id="myLog"><li ng-repeat="l in myLog" class="{{l.type}}">{{l.message}}</li></ul>',
|
||||
scope: {},
|
||||
compile: function() {
|
||||
return function(scope) {
|
||||
scope.myLog = $log.stack;
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
]).
|
||||
|
||||
config([
|
||||
'$provide',
|
||||
function($provide) {
|
||||
|
||||
$provide.decorator('$log', [
|
||||
'$delegate',
|
||||
function logDecorator($delegate) {
|
||||
|
||||
var myLog = {
|
||||
warn: function(msg) {
|
||||
log(msg, 'warn');
|
||||
},
|
||||
error: function(msg) {
|
||||
log(msg, 'error');
|
||||
},
|
||||
info: function(msg) {
|
||||
log(msg, 'info');
|
||||
},
|
||||
debug: function(msg) {
|
||||
log(msg, 'debug');
|
||||
},
|
||||
log: function(msg) {
|
||||
log(msg, 'log');
|
||||
},
|
||||
stack: []
|
||||
};
|
||||
|
||||
function log(msg, type) {
|
||||
myLog.stack.push({ type: type, message: msg.toString() });
|
||||
if (console && console[type]) console[type](msg);
|
||||
}
|
||||
|
||||
return myLog;
|
||||
|
||||
}
|
||||
]);
|
||||
|
||||
}
|
||||
]);
|
||||
</file>
|
||||
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<h1>Logs</h1>
|
||||
<my-log></my-log>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="style.css">
|
||||
li.warn { color: yellow; }
|
||||
li.error { color: red; }
|
||||
li.info { color: blue }
|
||||
li.log { color: black }
|
||||
li.debug { color: green }
|
||||
</file>
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should display log messages in dom', function() {
|
||||
element.all(by.repeater('l in myLog')).count().then(function(count) {
|
||||
expect(count).toEqual(6);
|
||||
});
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
||||
### Directive Decorator Example
|
||||
|
||||
Failed interpolated expressions in `ng-href` attributes can easily go unnoticed. We can decorate `ngHref` to warn us of
|
||||
those conditions.
|
||||
|
||||
<example module="urlDecorator" name="directive-decorator">
|
||||
<file name="script.js">
|
||||
angular.module('urlDecorator', []).
|
||||
|
||||
controller('Ctrl', ['$scope', function ($scope) {
|
||||
$scope.id = 3;
|
||||
$scope.warnCount = 0; // for testing
|
||||
}]).
|
||||
|
||||
config(['$provide', function($provide) {
|
||||
|
||||
// matchExpressions looks for interpolation markup in the directive attribute, extracts the expressions
|
||||
// from that markup (if they exist) and returns an array of those expressions
|
||||
function matchExpressions(str) {
|
||||
var exps = str.match(/{{([^}]+)}}/g);
|
||||
|
||||
// if there isn't any, get out of here
|
||||
if (exps === null) return;
|
||||
|
||||
exps = exps.map(function(exp) {
|
||||
var prop = exp.match(/[^{}]+/);
|
||||
return prop === null ? null : prop[0];
|
||||
});
|
||||
|
||||
return exps;
|
||||
}
|
||||
|
||||
// remember: directives must be selected by appending 'Directive' to the directive selector
|
||||
$provide.decorator('ngHrefDirective', [
|
||||
'$delegate',
|
||||
'$log',
|
||||
'$parse',
|
||||
function($delegate, $log, $parse) {
|
||||
|
||||
// store the original link fn
|
||||
var originalLinkFn = $delegate[0].link;
|
||||
|
||||
// replace the compile fn
|
||||
$delegate[0].compile = function(tElem, tAttr) {
|
||||
|
||||
// store the original exp in the directive attribute for our warning message
|
||||
var originalExp = tAttr.ngHref;
|
||||
|
||||
// get the interpolated expressions
|
||||
var exps = matchExpressions(originalExp);
|
||||
|
||||
// create and store the getters using $parse
|
||||
var getters = exps.map(function(el) {
|
||||
if (el) return $parse(el);
|
||||
});
|
||||
|
||||
return function newLinkFn(scope, elem, attr) {
|
||||
// fire the originalLinkFn
|
||||
originalLinkFn.apply($delegate[0], arguments);
|
||||
|
||||
// observe the directive attr and check the expressions
|
||||
attr.$observe('ngHref', function(val) {
|
||||
|
||||
// if we have getters and getters is an array...
|
||||
if (getters && angular.isArray(getters)) {
|
||||
|
||||
// loop through the getters and process them
|
||||
angular.forEach(getters, function(g, idx) {
|
||||
|
||||
// if val is truthy, then the warning won't log
|
||||
var val = angular.isFunction(g) ? g(scope) : true;
|
||||
if (!val) {
|
||||
$log.warn('NgHref Warning: "' + exps[idx] + '" in the expression "' + originalExp +
|
||||
'" is falsy!');
|
||||
|
||||
scope.warnCount++; // for testing
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
// get rid of the old link function since we return a link function in compile
|
||||
delete $delegate[0].link;
|
||||
|
||||
// return the $delegate
|
||||
return $delegate;
|
||||
|
||||
}
|
||||
|
||||
]);
|
||||
|
||||
}]);
|
||||
</file>
|
||||
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<a ng-href="/products/{{ id }}/view" id="id3">View Product {{ id }}</a>
|
||||
- <strong>id == 3</strong>, so no warning<br>
|
||||
<a ng-href="/products/{{ id + 5 }}/view" id="id8">View Product {{ id + 5 }}</a>
|
||||
- <strong>id + 5 == 8</strong>, so no warning<br>
|
||||
<a ng-href="/products/{{ someOtherId }}/view" id="someOtherId">View Product {{ someOtherId }}</a>
|
||||
- <strong style="background-color: #ffff00;">someOtherId == undefined</strong>, so warn<br>
|
||||
<a ng-href="/products/{{ someOtherId + 5 }}/view" id="someOtherId5">View Product {{ someOtherId + 5 }}</a>
|
||||
- <strong>someOtherId + 5 == 5</strong>, so no warning<br>
|
||||
<div>Warn Count: {{ warnCount }}</div>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should warn when an expression in the interpolated value is falsy', function() {
|
||||
var id3 = element(by.id('id3'));
|
||||
var id8 = element(by.id('id8'));
|
||||
var someOther = element(by.id('someOtherId'));
|
||||
var someOther5 = element(by.id('someOtherId5'));
|
||||
|
||||
expect(id3.getText()).toEqual('View Product 3');
|
||||
expect(id3.getAttribute('href')).toContain('/products/3/view');
|
||||
|
||||
expect(id8.getText()).toEqual('View Product 8');
|
||||
expect(id8.getAttribute('href')).toContain('/products/8/view');
|
||||
|
||||
expect(someOther.getText()).toEqual('View Product');
|
||||
expect(someOther.getAttribute('href')).toContain('/products//view');
|
||||
|
||||
expect(someOther5.getText()).toEqual('View Product 5');
|
||||
expect(someOther5.getAttribute('href')).toContain('/products/5/view');
|
||||
|
||||
expect(element(by.binding('warnCount')).getText()).toEqual('Warn Count: 1');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
||||
### Filter Decorator Example
|
||||
|
||||
Let's say we have created an app that uses the default format for many of our `Date` filters. Suddenly requirements have
|
||||
changed (that never happens) and we need all of our default dates to be `'shortDate'` instead of `'mediumDate'`.
|
||||
|
||||
<example module="filterDecorator" name="filter-decorator">
|
||||
<file name="script.js">
|
||||
angular.module('filterDecorator', []).
|
||||
|
||||
controller('Ctrl', ['$scope', function ($scope) {
|
||||
$scope.genesis = new Date(2010, 0, 5);
|
||||
$scope.ngConf = new Date(2016, 4, 4);
|
||||
}]).
|
||||
|
||||
config(['$provide', function($provide) {
|
||||
|
||||
$provide.decorator('dateFilter', [
|
||||
'$delegate',
|
||||
function dateDecorator($delegate) {
|
||||
|
||||
// store the original filter
|
||||
var originalFilter = $delegate;
|
||||
|
||||
// return our filter
|
||||
return shortDateDefault;
|
||||
|
||||
// shortDateDefault sets the format to shortDate if it is falsy
|
||||
function shortDateDefault(date, format, timezone) {
|
||||
if (!format) format = 'shortDate';
|
||||
|
||||
// return the result of the original filter
|
||||
return originalFilter(date, format, timezone);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
]);
|
||||
|
||||
}]);
|
||||
</file>
|
||||
|
||||
<file name="index.html">
|
||||
<div ng-controller="Ctrl">
|
||||
<div id="genesis">Initial Commit default to short date: {{ genesis | date }}</div>
|
||||
<div>ng-conf 2016 default short date: {{ ngConf | date }}</div>
|
||||
<div id="ngConf">ng-conf 2016 with full date format: {{ ngConf | date:'fullDate' }}</div>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should default date filter to short date format', function() {
|
||||
expect(element(by.id('genesis')).getText())
|
||||
.toMatch(/Initial Commit default to short date: \d{1,2}\/\d{1,2}\/\d{2}/);
|
||||
});
|
||||
|
||||
it('should still allow dates to be formatted', function() {
|
||||
expect(element(by.id('ngConf')).getText())
|
||||
.toMatch(/ng-conf 2016 with full date format\: [A-Za-z]+, [A-Za-z]+ \d{1,2}, \d{4}/);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
@@ -116,9 +116,6 @@ This restriction is intentional. It prevents accidental access to the global sta
|
||||
Instead use services like `$window` and `$location` in functions called from expressions. Such services
|
||||
provide mockable access to globals.
|
||||
|
||||
It is possible to access the context object using the identifier `this` and the locals object using the
|
||||
identifier `$locals`.
|
||||
|
||||
<example module="expressionExample">
|
||||
<file name="index.html">
|
||||
<div class="example2" ng-controller="ExampleController">
|
||||
|
||||
@@ -28,7 +28,4 @@ browsers, but it is up to you to test and decide whether it works for your parti
|
||||
To ensure your Angular application works on IE please consider:
|
||||
|
||||
1. Use `ng-style` tags instead of `style="{{ someCss }}"`. The latter works in Chrome and Firefox
|
||||
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
|
||||
2. For the `type` attribute of buttons, use `ng-attr-type` tags instead of
|
||||
`type="{{ someExpression }}"`. If using the latter, Internet Explorer overwrites the expression
|
||||
with `type="submit"` before Angular has a chance to interpolate it.
|
||||
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
|
||||
@@ -101,7 +101,7 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
|
||||
## Learning Resources
|
||||
|
||||
###Books
|
||||
### Books
|
||||
* [AngularJS: Up and Running](http://www.amazon.com/AngularJS-Running-Enhanced-Productivity-Structured/dp/1491901942) by Brad Green and Shyam Seshadri
|
||||
* [Mastering Web App Development](http://www.amazon.com/Mastering-Web-Application-Development-AngularJS/dp/1782161821) by Pawel Kozlowski and Pete Bacon Darwin
|
||||
* [AngularJS Directives](http://www.amazon.com/AngularJS-Directives-Alex-Vanston/dp/1783280336) by Alex Vanston
|
||||
@@ -113,7 +113,7 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
* [Responsive Web Design with AngularJS](http://www.amazon.com/Responsive-Design-AngularJS-Sandeep-Kumar/dp/178439842X) by Sandeep Kumar Patel
|
||||
* [Professional AngularJS](http://www.amazon.com/Professional-AngularJS-Valeri-Karpov/dp/1118832078/)
|
||||
|
||||
###Videos:
|
||||
### Videos:
|
||||
* [egghead.io](http://egghead.io/)
|
||||
* [Angular on YouTube](http://youtube.com/angularjs)
|
||||
* [Firebase Foundations for AngularJS](http://blog.watchandcode.com/firebase-foundations/)
|
||||
|
||||
@@ -3,246 +3,17 @@
|
||||
@sortOrder 550
|
||||
@description
|
||||
|
||||
# Migrating an App to a newer version
|
||||
|
||||
Minor version releases in AngularJS introduce several breaking changes that may require changes to your
|
||||
application's source code; for instance from 1.0 to 1.2 and from 1.2 to 1.3.
|
||||
|
||||
Although we try to avoid breaking changes, there are some cases where it is unavoidable:
|
||||
Although we try to avoid breaking changes, there are some cases where it is unavoidable.
|
||||
|
||||
* AngularJS has undergone thorough security reviews to make applications safer by default,
|
||||
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.
|
||||
|
||||
|
||||
|
||||
## Contents
|
||||
|
||||
<ul class="nav nav-list">
|
||||
<li>{@link guide/migration#migrating-from-1-4-to-1-5 Migrating from 1.4 to 1.5}</li>
|
||||
<li>{@link guide/migration#migrating-from-1-3-to-1-4 Migrating from 1.3 to 1.4}</li>
|
||||
<li>{@link guide/migration#migrating-from-1-2-to-1-3 Migrating from 1.2 to 1.3}</li>
|
||||
<li>{@link guide/migration#migrating-from-1-0-to-1-2 Migrating from 1.0 to 1.2}</li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Migrating from 1.4 to 1.5
|
||||
|
||||
Angular 1.5 takes a big step towards preparing developers for a smoother transition to Angular 2 in
|
||||
the future. Architecturing your applications using components, multi-slot transclusion, one-way
|
||||
bindings in isolate scopes, using lifecycle hooks in directive controllers and relying on native ES6
|
||||
features (such as classes and arrow functions) are now all possible with Angular 1.5.
|
||||
|
||||
|
||||
This release includes numerous bug and security fixes, as well as performance improvements to core
|
||||
services, directives, filters and helper functions. Existing applications can start enjoying the
|
||||
benefits of such changes in `$compile`, `$parse`, `$animate`, `$animateCss`, `$sanitize`, `ngOptions`,
|
||||
`currencyFilter`, `numberFilter`, `copy()` (to name but a few) without any change in code.
|
||||
|
||||
New features have been added to more than a dozen services, directives and filters across 8 modules.
|
||||
Among them, a few stand out:
|
||||
|
||||
* `angular.component()`: Introducing "components", a special sort of directive that are easy to
|
||||
configure and promote best practices (plus can bring Angular 1 applications closer to Angular 2's
|
||||
style of architecture).
|
||||
* Multi-slot transclusion: Enabling the design of more powerful and complex UI elements with a much
|
||||
simpler configuration and reduced boilerplate.
|
||||
* `$onInit` lifecycle hook: Introducing a new lifecycle hook for directive controllers, called after
|
||||
all required controllers have been constructed. This enables access to required controllers from
|
||||
a directive's controller, without having to rely on the linking function.
|
||||
* `ngAnimateSwap`: A new directive in `ngAnimate`, making it super easy to create rotating
|
||||
banner-like components.
|
||||
* Testing helpers: New helper functions in `ngMock`, simplifying testing for animations, component
|
||||
controllers and routing.
|
||||
|
||||
Also, notable is the improved support for ES6 features, such as classes and arrow functions. These
|
||||
features are now more reliably detected and correctly handled within the core.
|
||||
|
||||
|
||||
All this goodness doesn't come without a price, though. Below is a list of breaking changes (grouped
|
||||
by module) that need to be taken into account while migrating from 1.4. Fortunately, the majority of
|
||||
them should have a pretty low impact on most applications.
|
||||
|
||||
|
||||
### Core
|
||||
|
||||
We tried to keep the breaking changes inside the core components to a bare minimum. Still, a few of
|
||||
them were unavoidable.
|
||||
|
||||
#### Services (`$parse`)
|
||||
|
||||
Due to [0ea53503](https://github.com/angular/angular.js/commit/0ea535035a3a1a992948490c3533bffb83235052),
|
||||
a new special property, `$locals`, will be available for accessing the locals from an expression.
|
||||
This is a breaking change, only if a `$locals` property does already exist (and needs to be
|
||||
referenced) either on the `scope` or on the `locals` object. Your expressions should be changed to
|
||||
access such existing properties as `this.$locals` and `$locals.$locals` respectively.
|
||||
|
||||
|
||||
#### Directives (`ngOptions`)
|
||||
|
||||
A fair amount of work has been put into the `ngOptions` directive, fixing bugs and corner-cases and
|
||||
neutralizing browser quirks. A couple of breaking changes were made in the process:
|
||||
|
||||
Due to [b71d7c3f](https://github.com/angular/angular.js/commit/b71d7c3f3c04e65b02d88b33c22dd90ae3cdfc27),
|
||||
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 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`.
|
||||
|
||||
Due to [ded25187](https://github.com/angular/angular.js/commit/ded2518756d4409fdfda0d4af243f2125bea01b5),
|
||||
`ngOptions` now explicitly requires `ngModel` on the same element, thus an error will be thrown if
|
||||
`ngModel` is not found. Previously, `ngOptions` would silently fail, which could lead to
|
||||
hard-to-debug errors.
|
||||
This is not expected to have any significant impact on applications, since `ngOptions` didn't work
|
||||
without `ngModel` before either. The main difference is that now it will fail with a more
|
||||
informative error message.
|
||||
|
||||
|
||||
#### Filters (`orderBy`)
|
||||
|
||||
Due to [2a85a634](https://github.com/angular/angular.js/commit/2a85a634f86c84f15b411ce009a3515fca7ba580),
|
||||
passing a non-array-like value (other than `undefined` or `null`) through the `orderBy` filter will
|
||||
throw an error. Previously, the input was returned unchanged, which could lead to hard-to-spot bugs
|
||||
and was not consistent with other filters (e.g. `filter`).
|
||||
Objects considered array-like include: arrays, array subclasses, strings, NodeLists,
|
||||
jqLite/jQuery collections
|
||||
|
||||
|
||||
### ngAria
|
||||
|
||||
Due to [d06431e](https://github.com/angular/angular.js/commit/d06431e5309bb0125588877451dc79b935808134),
|
||||
the `ngAria`-enhanced directives (e.g. `ngModel`, `ngDisabled` etc) will not apply ARIA attributes
|
||||
to native inputs, unless necessary. Previously, ARIA attributes were always applied to native
|
||||
inputs, despite this being unnecessary in most cases.
|
||||
In the context of `ngAria`, elements considered "native inputs" include:
|
||||
`<a>`, `<button>`, `<details>`, `<input>`, `<select>`, `<summary>`, `<textarea>`
|
||||
|
||||
This change will not affect the accessibility of your applications (since native inputs are
|
||||
accessible by default), but if you relied on ARIA attributes being present on native inputs (for
|
||||
whatever reason), you'll have to add and update them manually.
|
||||
|
||||
Additionally, the `aria-multiline` attribute, which was previously added to elements with a `type`
|
||||
or `role` of `textbox`, will not be added anymore, since there is no way `ngAria` can tell if the
|
||||
textbox element is multiline or not.
|
||||
If you relied on `aria-multiline="true"` being automatically added by `ngAria`, you need to apply it
|
||||
yourself. E.g. change your code from `<div role="textbox" ng-model="..." ...>` to
|
||||
`<div role="textbox" ng-model="..." ... aria-multiline="true">`.
|
||||
|
||||
|
||||
### ngMessages (`ngMessage`)
|
||||
|
||||
Due to [4971ef12](https://github.com/angular/angular.js/commit/4971ef12d4c2c268cb8d26f90385dc96eba19db8),
|
||||
the `ngMessage` directive is now compiled with a priority of 1, which means directives on the same
|
||||
element as `ngMessage` with a priority lower than 1 will be applied when `ngMessage` calls its
|
||||
`$transclude` function. Previously, they were applied during the initial compile phase and were
|
||||
passed the comment element created by the transclusion of `ngMessage`.
|
||||
If you have custom directives that relied on the previous behavior, you need to give them a priority
|
||||
of 1 or greater.
|
||||
|
||||
|
||||
### ngResource (`$resource`)
|
||||
|
||||
The `$resource` service underwent a minor internal refactoring to finally solve a long-standing bug
|
||||
preventing requests from being cancelled using promises. Due to the nature of `$resource`'s
|
||||
configuration, it was not possible to follow the `$http` convention. A new `$cancelRequest()` method
|
||||
was introduced instead.
|
||||
|
||||
Due to [98528be3](https://github.com/angular/angular.js/commit/98528be311b48269ba0e15ba4e3e2ad9b89693a9),
|
||||
using a promise as `timeout` in `$resource` is no longer supported and will log a warning. This is
|
||||
hardly expected to affect the behavior of your application, since a promise as `timeout` didn't work
|
||||
before either, but it will now warn you explicitly when trying to pass one.
|
||||
If you need to be able to cancel pending requests, you can now use the new `$cancelRequest()` that
|
||||
will be available on `$resource` instances.
|
||||
|
||||
|
||||
### ngRoute (`ngView`)
|
||||
|
||||
Due to [983b0598](https://github.com/angular/angular.js/commit/983b0598121a8c5a3a51a30120e114d7e3085d4d),
|
||||
a new property will be available on the scope of the route, allowing easy access to the route's
|
||||
resolved values from the view's template. The default name for this property is `$resolve`. This is
|
||||
a breaking change, only if a `$resolve` property is already available on the scope, in which case
|
||||
the existing property will be hidden or overwritten.
|
||||
To fix this, you should choose a custom name for this property, that does not collide with other
|
||||
properties on the scope, by specifying the `resolveAs` property on the route.
|
||||
|
||||
|
||||
### ngSanitize (`$sanitize`, `linky`)
|
||||
|
||||
The HTML sanitizer has been re-implemented using inert documents, increasing security, fixing some
|
||||
corner-cases that were difficult to handle and reducing its size by about 20% (in terms of loc). In
|
||||
order to make it more secure by default, a couple of breaking changes have been introduced:
|
||||
|
||||
Due to [181fc567](https://github.com/angular/angular.js/commit/181fc567d873df065f1e84af7225deb70a8d2eb9),
|
||||
SVG support in `$sanitize` is now an opt-in feature (i.e. disabled by default), as it could make
|
||||
an application vulnerable to click-hijacking attacks. If your application relies on it, you can
|
||||
still turn it on with `$sanitizeProvider.enableSvg(true)`, but you extra precautions need to be
|
||||
taken in order to keep your application secure. Read the documentation for more information about
|
||||
the dangers and ways to mitigate them.
|
||||
|
||||
Due to [7a668cdd](https://github.com/angular/angular.js/commit/7a668cdd7d08a7016883eb3c671cbcd586223ae8),
|
||||
the `$sanitize` service will now remove instances of the `<use>` tag from the content passed to it.
|
||||
This element is used to import external SVG resources, which is a security risk as the `$sanitize`
|
||||
service does not have access to the resource in order to sanitize it.
|
||||
|
||||
Similarly, due to [234053fc](https://github.com/angular/angular.js/commit/234053fc9ad90e0d05be7e8359c6af66be94c094),
|
||||
the `$sanitize` service will now also remove instances of the `usemap` attribute from any elements
|
||||
passed to it. This attribute is used to reference another element by `name` or `id`. Since the
|
||||
`name` and `id` attributes are already blacklisted, a sanitized `usemap` attribute could only
|
||||
reference unsanitized content, which is a security risk.
|
||||
|
||||
Due to [98c2db7f](https://github.com/angular/angular.js/commit/98c2db7f9c2d078a408576e722407d518c7ee10a),
|
||||
passing a non-string value (other than `undefined` or `null`) through the `linky` filter will throw
|
||||
an error. This is not expected to have any significant impact on applications, since the input was
|
||||
always assumed to be of type 'string', so passing non-string values never worked correctly anyway.
|
||||
The main difference is that now it will fail faster and with a more informative error message.
|
||||
|
||||
|
||||
## ngTouch (`ngClick`)
|
||||
|
||||
Due to [0dfc1dfe](https://github.com/angular/angular.js/commit/0dfc1dfebf26af7f951f301c4e3848ac46f05d7f),
|
||||
the `ngClick` override directive from the `ngTouch` module is **deprecated and disabled by default**.
|
||||
This means that on touch-based devices, users might now experience a 300ms delay before a click
|
||||
event is fired.
|
||||
|
||||
If you rely on this directive, you can still enable it using
|
||||
`$touchProvider.ngClickOverrideEnabled()`:
|
||||
|
||||
```js
|
||||
angular.module('myApp').config(function($touchProvider) {
|
||||
$touchProvider.ngClickOverrideEnabled(true);
|
||||
});
|
||||
```
|
||||
|
||||
Going forward, we recommend using [FastClick](https://github.com/ftlabs/fastclick) or perhaps one of
|
||||
the [Angular 3rd party touch-related modules](http://ngmodules.org/tags/touch) that provide similar
|
||||
functionality.
|
||||
|
||||
Also note that modern browsers already remove the 300ms delay under some circumstances:
|
||||
|
||||
- **Chrome and Firefox for Android** remove the 300ms delay when the well-known
|
||||
`<meta name="viewport" content="width=device-width">` is set.
|
||||
- **Internet Explorer** removes the delay, when the `touch-action` css property is set to `none` or
|
||||
`manipulation`.
|
||||
- Since **iOS 8, Safari** removes the delay on so-called "slow taps".
|
||||
|
||||
For more info on the topic, you can take a look at this
|
||||
[article by Telerik](http://developer.telerik.com/featured/300-ms-click-delay-ios-8/).
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** This change does **not** affect the `ngSwipe` directive.
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Migrating from 1.3 to 1.4
|
||||
# Migrating from 1.3 to 1.4
|
||||
|
||||
Angular 1.4 fixes major animation issues and introduces a new API for `ngCookies`. Further, there
|
||||
are changes to `ngMessages`, `$compile`, `ngRepeat`, `ngOptions `and some fixes to core filters:
|
||||
@@ -266,7 +37,7 @@ relatively straightforward otherwise.
|
||||
|
||||
|
||||
|
||||
### Animation (`ngAnimate`)
|
||||
## Animation (`ngAnimate`)
|
||||
|
||||
Animations in 1.4 have been refactored internally, but the API has stayed much the same. There are, however,
|
||||
some breaking changes that need to be addressed when upgrading to 1.4.
|
||||
@@ -365,9 +136,9 @@ class based animations (animations triggered via ngClass) in order to ensure tha
|
||||
|
||||
|
||||
|
||||
### Forms (`ngMessages`, `ngOptions`, `select`)
|
||||
## Forms (`ngMessages`, `ngOptions`, `select`)
|
||||
|
||||
#### ngMessages
|
||||
### ngMessages
|
||||
The ngMessages module has also been subject to an internal refactor to allow it to be more flexible
|
||||
and compatible with dynamic message data. The `ngMessage` directive now supports a new attribute
|
||||
called `ng-message-exp` which will evaluate an expression and will keep track of that expression
|
||||
@@ -418,7 +189,7 @@ ctrl.getMessages = function($index) {
|
||||
}
|
||||
```
|
||||
|
||||
#### ngOptions
|
||||
### ngOptions
|
||||
|
||||
The `ngOptions` directive has also been refactored and as a result some long-standing bugs
|
||||
have been fixed. The breaking changes are comparatively minor and should not affect most applications.
|
||||
@@ -442,7 +213,7 @@ setting the ngOptions attribute expression after the element is compiled, will n
|
||||
This worked previously because the ngOptions logic was part of the select directive, while
|
||||
it is now implemented in the ngOptions directive itself.
|
||||
|
||||
#### select
|
||||
### select
|
||||
|
||||
Due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
|
||||
the `select` directive will now use strict comparison of the `ngModel` scope value against `option`
|
||||
@@ -473,7 +244,6 @@ ngModelCtrl.$formatters.push(function(value) {
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### form
|
||||
|
||||
Due to [94533e57](https://github.com/angular/angular.js/commit/94533e570673e6b2eb92073955541fa289aabe02),
|
||||
@@ -514,9 +284,9 @@ angular.module('myApp').directive('form', function() {
|
||||
});
|
||||
```
|
||||
|
||||
### Templating (`ngRepeat`, `$compile`)
|
||||
## Templating (`ngRepeat`, `$compile`)
|
||||
|
||||
#### ngRepeat
|
||||
### ngRepeat
|
||||
|
||||
Due to [c260e738](https://github.com/angular/angular.js/commit/c260e7386391877625eda086480de73e8a0ba921),
|
||||
previously, the order of items when using ngRepeat to iterate over object properties was guaranteed to be consistent
|
||||
@@ -535,7 +305,7 @@ https://github.com/petebacondarwin/angular-toArrayFilter
|
||||
or some other mechanism, and then sort them manually in the order you need.
|
||||
|
||||
|
||||
#### $compile
|
||||
### $compile
|
||||
|
||||
Due to [6a38dbfd](https://github.com/angular/angular.js/commit/6a38dbfd3c34c8f9efff503d17eb3cbeb666d422),
|
||||
previously, '&' expressions would always set up a function in the isolate scope. Now, if the binding
|
||||
@@ -546,7 +316,7 @@ returning an object from a controller constructor function will now override the
|
||||
controllerAs method will no longer get the this reference, but the returned object.
|
||||
|
||||
|
||||
### Cookies (`ngCookies`)
|
||||
## Cookies (`ngCookies`)
|
||||
|
||||
Due to [38fbe3ee](https://github.com/angular/angular.js/commit/38fbe3ee8370fc449b82d80df07b5c2ed2cd5fbe),
|
||||
`$cookies` will no longer expose properties that represent the current browser cookie
|
||||
@@ -585,7 +355,7 @@ delegates calls.
|
||||
|
||||
|
||||
|
||||
### Server Requests (`$http`)
|
||||
## Server Requests (`$http`)
|
||||
|
||||
Due to [5da1256](https://github.com/angular/angular.js/commit/5da1256fc2812d5b28fb0af0de81256054856369),
|
||||
`transformRequest` functions can no longer modify request headers.
|
||||
@@ -618,9 +388,9 @@ $http.get(url, {
|
||||
|
||||
|
||||
|
||||
### Filters (`filter`, `limitTo`)
|
||||
## Filters (`filter`, `limitTo`)
|
||||
|
||||
#### `filter` filter
|
||||
### `filter` filter
|
||||
Due to [cea8e751](https://github.com/angular/angular.js/commit/cea8e75144e6910b806b63a6ec2a6d118316fddd),
|
||||
the `filter` filter will throw an error when used with a non-array. Beforehand it would silently
|
||||
return an empty array.
|
||||
@@ -628,7 +398,7 @@ return an empty array.
|
||||
If necessary, this can be worked around by converting an object to an array,
|
||||
using a filter such as https://github.com/petebacondarwin/angular-toArrayFilter.
|
||||
|
||||
#### `limitTo` filter
|
||||
### `limitTo` filter
|
||||
Due to [a3c3bf33](https://github.com/angular/angular.js/commit/a3c3bf3332e5685dc319c46faef882cb6ac246e1),
|
||||
the limitTo filter has changed behavior when the provided limit value is invalid.
|
||||
Now, instead of returning empty object/array, it returns unchanged input.
|
||||
@@ -637,9 +407,9 @@ Now, instead of returning empty object/array, it returns unchanged input.
|
||||
|
||||
|
||||
|
||||
## Migrating from 1.2 to 1.3
|
||||
# Migrating from 1.2 to 1.3
|
||||
|
||||
### Controllers
|
||||
## Controllers
|
||||
|
||||
Due to [3f2232b5](https://github.com/angular/angular.js/commit/3f2232b5a181512fac23775b1df4a6ebda67d018),
|
||||
`$controller` will no longer look for controllers on `window`.
|
||||
@@ -677,7 +447,7 @@ angular.module('myModule').config(['$controllerProvider', function($controllerPr
|
||||
}]);
|
||||
```
|
||||
|
||||
### Angular Expression Parsing (`$parse` + `$interpolate`)
|
||||
## Angular Expression Parsing (`$parse` + `$interpolate`)
|
||||
|
||||
- due to [77ada4c8](https://github.com/angular/angular.js/commit/77ada4c82d6b8fc6d977c26f3cdb48c2f5fbe5a5),
|
||||
|
||||
@@ -735,7 +505,7 @@ expression parser; there are six of them: false, null, undefined, NaN, 0 and "".
|
||||
|
||||
|
||||
|
||||
### Miscellaneous Angular helpers
|
||||
## Miscellaneous Angular helpers
|
||||
|
||||
- **Angular.copy:** due to [b59b04f9](https://github.com/angular/angular.js/commit/b59b04f98a0b59eead53f6a53391ce1bbcbe9b57),
|
||||
|
||||
@@ -766,14 +536,15 @@ This change also makes our forEach behave more like Array#forEach.
|
||||
|
||||
|
||||
- **angular.toJson:** due to [c054288c](https://github.com/angular/angular.js/commit/c054288c9722875e3595e6e6162193e0fb67a251),
|
||||
`toJson()` will no longer strip properties starting with a single `$`. If you relied on
|
||||
`toJson()`'s stripping these types of properties before, you will have to do it manually now.
|
||||
It will still strip properties starting with `$$` though.
|
||||
|
||||
If you expected `toJson` to strip these types of properties before, you will have to
|
||||
manually do this yourself now.
|
||||
|
||||
|
||||
|
||||
|
||||
### jqLite / JQuery
|
||||
|
||||
## jqLite / JQuery
|
||||
|
||||
- **jqLite:** due to [a196c8bc](https://github.com/angular/angular.js/commit/a196c8bca82a28c08896d31f1863cf4ecd11401c),
|
||||
previously it was possible to set jqLite data on Text/Comment
|
||||
@@ -789,7 +560,7 @@ jQuery. We don't expect that app code actually depends on this accidental featur
|
||||
|
||||
|
||||
|
||||
### Angular HTML Compiler (`$compile`)
|
||||
## Angular HTML Compiler (`$compile`)
|
||||
|
||||
|
||||
- due to [2ee29c5d](https://github.com/angular/angular.js/commit/2ee29c5da81ffacdc1cabb438f5d125d5e116cb9),
|
||||
@@ -886,7 +657,7 @@ link: function(scope, element, attr) {
|
||||
|
||||
|
||||
|
||||
### Forms, Inputs and ngModel
|
||||
## Forms, Inputs and ngModel
|
||||
|
||||
- due to [1be9bb9d](https://github.com/angular/angular.js/commit/1be9bb9d3527e0758350c4f7417a4228d8571440),
|
||||
|
||||
@@ -966,7 +737,7 @@ $scope.resetWithCancel = function (e) {
|
||||
See [c90cefe1614](https://github.com/angular/angular.js/commit/c90cefe16142d973a123e945fc9058e8a874c357)
|
||||
|
||||
|
||||
### Scopes and Digests (`$scope`)
|
||||
## Scopes and Digests (`$scope`)
|
||||
|
||||
- due to [8c6a8171](https://github.com/angular/angular.js/commit/8c6a8171f9bdaa5cdabc0cc3f7d3ce10af7b434d),
|
||||
Scope#$id is now of type number rather than string. Since the
|
||||
@@ -986,7 +757,7 @@ anyone.
|
||||
|
||||
|
||||
|
||||
### Server Requests (`$http`, `$resource`)
|
||||
## Server Requests (`$http`, `$resource`)
|
||||
- **$http:** due to [ad4336f9](https://github.com/angular/angular.js/commit/ad4336f9359a073e272930f8f9bcd36587a8648f),
|
||||
|
||||
|
||||
@@ -1053,7 +824,7 @@ More details on the new interceptors API (which has been around as of v1.1.4) ca
|
||||
|
||||
|
||||
|
||||
### Modules and Injector (`$inject`)
|
||||
## Modules and Injector (`$inject`)
|
||||
|
||||
- due to [c0b4e2db](https://github.com/angular/angular.js/commit/c0b4e2db9cbc8bc3164cedc4646145d3ab72536e),
|
||||
|
||||
@@ -1095,7 +866,7 @@ app. This is no longer possible within a single module.
|
||||
|
||||
|
||||
|
||||
### Animation (`ngAnimate`)
|
||||
## Animation (`ngAnimate`)
|
||||
|
||||
|
||||
- due to [1cb8584e](https://github.com/angular/angular.js/commit/1cb8584e8490ecdb1b410a8846c4478c6c2c0e53),
|
||||
@@ -1117,7 +888,7 @@ to:
|
||||
|
||||
Any class-based animation code that makes use of transitions
|
||||
and uses the setup CSS classes (such as class-add and class-remove) must now
|
||||
provide an empty transition value to ensure that its styling is applied right
|
||||
provide a empty transition value to ensure that its styling is applied right
|
||||
away. In other words if your animation code is expecting any styling to be
|
||||
applied that is defined in the setup class then it will not be applied
|
||||
"instantly" unless a `transition:0s none` value is present in the styling
|
||||
@@ -1148,7 +919,7 @@ After:
|
||||
Please view the documentation for ngAnimate for more info.
|
||||
|
||||
|
||||
### Testing
|
||||
## Testing
|
||||
|
||||
- due to [85880a64](https://github.com/angular/angular.js/commit/85880a64900fa22a61feb926bf52de0965332ca5), some deprecated features of
|
||||
Protractor tests no longer work.
|
||||
@@ -1187,7 +958,7 @@ or simply use:
|
||||
var el = element(by.repeater('foo in foos').row(2))
|
||||
|
||||
|
||||
### Internet Explorer 8
|
||||
## Internet Explorer 8
|
||||
|
||||
- due to [eaa1d00b](https://github.com/angular/angular.js/commit/eaa1d00b24008f590b95ad099241b4003688cdda),
|
||||
As communicated before, IE8 is no longer supported.
|
||||
@@ -1197,7 +968,7 @@ or simply use:
|
||||
|
||||
|
||||
|
||||
## Migrating from 1.0 to 1.2
|
||||
# Migrating from 1.0 to 1.2
|
||||
|
||||
|
||||
<div class="alert alert-warning">
|
||||
@@ -1240,7 +1011,7 @@ below should still apply, but you may want to consult the
|
||||
</ul>
|
||||
|
||||
|
||||
### ngRoute has been moved into its own module
|
||||
## ngRoute has been moved into its own module
|
||||
|
||||
Just like `ngResource`, `ngRoute` is now its own module.
|
||||
|
||||
@@ -1271,7 +1042,7 @@ var myApp = angular.module('myApp', ['ngRoute', 'someOtherModule']);
|
||||
See [5599b55b](https://github.com/angular/angular.js/commit/5599b55b04788c2e327d7551a4a699d75516dd21).
|
||||
|
||||
|
||||
### Templates no longer automatically unwrap promises
|
||||
## Templates no longer automatically unwrap promises
|
||||
|
||||
`$parse` and templates in general will no longer automatically unwrap promises.
|
||||
|
||||
@@ -1305,7 +1076,7 @@ See [5dc35b52](https://github.com/angular/angular.js/commit/5dc35b527b3c99f6544b
|
||||
[b6a37d11](https://github.com/angular/angular.js/commit/b6a37d112b3e1478f4d14a5f82faabf700443748).
|
||||
|
||||
|
||||
### Syntax for named wildcard parameters changed in `$route`
|
||||
## Syntax for named wildcard parameters changed in `$route`
|
||||
|
||||
To migrate the code, follow the example below. Here, `*highlight` becomes `:highlight*`
|
||||
|
||||
@@ -1326,7 +1097,7 @@ $routeProvider.when('/Book1/:book/Chapter/:chapter/:highlight*/edit',
|
||||
See [04cebcc1](https://github.com/angular/angular.js/commit/04cebcc133c8b433a3ac5f72ed19f3631778142b).
|
||||
|
||||
|
||||
### You can only bind one expression to `*[src]`, `*[ng-src]` or `action`
|
||||
## You can only bind one expression to `*[src]`, `*[ng-src]` or `action`
|
||||
|
||||
With the exception of `<a>` and `<img>` elements, you cannot bind more than one expression to the
|
||||
`src` or `action` attribute of elements.
|
||||
@@ -1401,7 +1172,7 @@ scope.getIframeSrc = function() {
|
||||
See [38deedd6](https://github.com/angular/angular.js/commit/38deedd6e3d806eb8262bb43f26d47245f6c2739).
|
||||
|
||||
|
||||
### Interpolations inside DOM event handlers are now disallowed
|
||||
## Interpolations inside DOM event handlers are now disallowed
|
||||
|
||||
DOM event handlers execute arbitrary Javascript code. Using an interpolation for such handlers
|
||||
means that the interpolated value is a JS string that is evaluated. Storing or generating such
|
||||
@@ -1428,7 +1199,7 @@ HTML: <div ng-click="foo()">
|
||||
See [39841f2e](https://github.com/angular/angular.js/commit/39841f2ec9b17b3b2920fd1eb548d444251f4f56).
|
||||
|
||||
|
||||
### Directives cannot end with -start or -end
|
||||
## Directives cannot end with -start or -end
|
||||
|
||||
This change was necessary to enable multi-element directives. The best fix is to rename existing
|
||||
directives so that they don't end with these suffixes.
|
||||
@@ -1436,7 +1207,7 @@ directives so that they don't end with these suffixes.
|
||||
See [e46100f7](https://github.com/angular/angular.js/commit/e46100f7097d9a8f174bdb9e15d4c6098395c3f2).
|
||||
|
||||
|
||||
### In $q, promise.always has been renamed promise.finally
|
||||
## In $q, promise.always has been renamed promise.finally
|
||||
|
||||
The reason for this change is to align `$q` with the [Q promise
|
||||
library](https://github.com/kriskowal/q), despite the fact that this makes it a bit more difficult
|
||||
@@ -1468,7 +1239,7 @@ $http.get('/foo')['finally'](doSomething);
|
||||
See [f078762d](https://github.com/angular/angular.js/commit/f078762d48d0d5d9796dcdf2cb0241198677582c).
|
||||
|
||||
|
||||
### ngMobile is now ngTouch
|
||||
## ngMobile is now ngTouch
|
||||
|
||||
Many touch-enabled devices are not mobile devices, so we decided to rename this module to better
|
||||
reflect its concerns.
|
||||
@@ -1479,7 +1250,7 @@ To migrate, replace all references to `ngMobile` with `ngTouch` and `angular-mob
|
||||
See [94ec84e7](https://github.com/angular/angular.js/commit/94ec84e7b9c89358dc00e4039009af9e287bbd05).
|
||||
|
||||
|
||||
### resource.$then has been removed
|
||||
## resource.$then has been removed
|
||||
|
||||
Resource instances do not have a `$then` function anymore. Use the `$promise.then` instead.
|
||||
|
||||
@@ -1498,7 +1269,7 @@ Resource.query().$promise.then(callback);
|
||||
See [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d).
|
||||
|
||||
|
||||
### Resource methods return the promise
|
||||
## Resource methods return the promise
|
||||
|
||||
Methods of a resource instance return the promise rather than the instance itself.
|
||||
|
||||
@@ -1518,7 +1289,7 @@ resource.chaining = true;
|
||||
See [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d).
|
||||
|
||||
|
||||
### Resource promises are resolved with the resource instance
|
||||
## Resource promises are resolved with the resource instance
|
||||
|
||||
On success, the resource promise is resolved with the resource instance rather than HTTP response object.
|
||||
|
||||
@@ -1549,7 +1320,7 @@ var Resource = $resource('/url', {}, {
|
||||
See [05772e15](https://github.com/angular/angular.js/commit/05772e15fbecfdc63d4977e2e8839d8b95d6a92d).
|
||||
|
||||
|
||||
### $location.search supports multiple keys
|
||||
## $location.search supports multiple keys
|
||||
|
||||
{@link ng.$location#search `$location.search`} now supports multiple keys with the
|
||||
same value provided that the values are stored in an array.
|
||||
@@ -1566,7 +1337,7 @@ passing it to `$location`.
|
||||
See [80739409](https://github.com/angular/angular.js/commit/807394095b991357225a03d5fed81fea5c9a1abe).
|
||||
|
||||
|
||||
### ngBindHtmlUnsafe has been removed and replaced by ngBindHtml
|
||||
## ngBindHtmlUnsafe has been removed and replaced by ngBindHtml
|
||||
|
||||
`ngBindHtml` provides `ngBindHtmlUnsafe` like
|
||||
behavior (evaluate an expression and innerHTML the result into the DOM) when bound to the result
|
||||
@@ -1582,7 +1353,7 @@ trusted.
|
||||
See [dae69473](https://github.com/angular/angular.js/commit/dae694739b9581bea5dbc53522ec00d87b26ae55).
|
||||
|
||||
|
||||
### Form names that are expressions are evaluated
|
||||
## Form names that are expressions are evaluated
|
||||
|
||||
If you have form names that will evaluate as an expression:
|
||||
|
||||
@@ -1614,7 +1385,7 @@ Supporting the previous behavior offers no benefit.
|
||||
See [8ea802a1](https://github.com/angular/angular.js/commit/8ea802a1d23ad8ecacab892a3a451a308d9c39d7).
|
||||
|
||||
|
||||
### hasOwnProperty disallowed as an input name
|
||||
## hasOwnProperty disallowed as an input name
|
||||
|
||||
Inputs with name equal to `hasOwnProperty` are not allowed inside form or ngForm directives.
|
||||
|
||||
@@ -1625,7 +1396,7 @@ and bad practice. To migrate, change your input name.
|
||||
See [7a586e5c](https://github.com/angular/angular.js/commit/7a586e5c19f3d1ecc3fefef084ce992072ee7f60).
|
||||
|
||||
|
||||
### Directives: Order of postLink functions reversed
|
||||
## Directives: Order of postLink functions reversed
|
||||
|
||||
The order of postLink fn is now mirror opposite of the order in which corresponding preLinking and compile functions execute.
|
||||
|
||||
@@ -1681,7 +1452,7 @@ attribute interpolation directive was adjusted.
|
||||
See [31f190d4](https://github.com/angular/angular.js/commit/31f190d4d53921d32253ba80d9ebe57d6c1de82b).
|
||||
|
||||
|
||||
### Directive priority
|
||||
## Directive priority
|
||||
|
||||
the priority of ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView has changed. This could affect directives that explicitly specify their priority.
|
||||
|
||||
@@ -1699,7 +1470,7 @@ ngView | 1000 | 400
|
||||
See [b7af76b4](https://github.com/angular/angular.js/commit/b7af76b4c5aa77648cc1bfd49935b48583419023).
|
||||
|
||||
|
||||
### ngScenario
|
||||
## ngScenario
|
||||
|
||||
browserTrigger now uses an eventData object instead of direct parameters for mouse events.
|
||||
To migrate, place the `keys`,`x` and `y` parameters inside of an object and place that as the
|
||||
@@ -1708,7 +1479,7 @@ third parameter for the browserTrigger function.
|
||||
See [28f56a38](https://github.com/angular/angular.js/commit/28f56a383e9d1ff378e3568a3039e941c7ffb1d8).
|
||||
|
||||
|
||||
### ngInclude and ngView replace its entire element on update
|
||||
## ngInclude and ngView replace its entire element on update
|
||||
|
||||
Previously `ngInclude` and `ngView` only updated its element's content. Now these directives will
|
||||
recreate the element every time a new content is included.
|
||||
@@ -1720,7 +1491,7 @@ See [7d69d52a](https://github.com/angular/angular.js/commit/7d69d52acff8578e0f7d
|
||||
[aa2133ad](https://github.com/angular/angular.js/commit/aa2133ad818d2e5c27cbd3933061797096356c8a).
|
||||
|
||||
|
||||
### URLs are now sanitized against a whitelist
|
||||
## URLs are now sanitized against a whitelist
|
||||
|
||||
A whitelist configured via `$compileProvider` can be used to configure what URLs are considered safe.
|
||||
By default all common protocol prefixes are whitelisted including `data:` URIs with mime types `image/*`.
|
||||
@@ -1730,7 +1501,7 @@ See [1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d612868
|
||||
[3e39ac7e](https://github.com/angular/angular.js/commit/3e39ac7e1b10d4812a44dad2f959a93361cd823b).
|
||||
|
||||
|
||||
### Isolate scope only exposed to directives with `scope` property
|
||||
## Isolate scope only exposed to directives with `scope` property
|
||||
|
||||
If you declare a scope option on a directive, that directive will have an
|
||||
[isolate scope](https://github.com/angular/angular.js/wiki/Understanding-Scopes). In Angular 1.0, if a
|
||||
@@ -1803,7 +1574,7 @@ See [909cabd3](https://github.com/angular/angular.js/commit/909cabd36d779598763c
|
||||
[#2500](https://github.com/angular/angular.js/issues/2500).
|
||||
|
||||
|
||||
### Change to interpolation priority
|
||||
## Change to interpolation priority
|
||||
|
||||
Previously, the interpolation priority was `-100` in 1.2.0-rc.2, and `100` before 1.2.0-rc.2.
|
||||
Before this change the binding was setup in the post-linking phase.
|
||||
@@ -1816,7 +1587,7 @@ See [79223eae](https://github.com/angular/angular.js/commit/79223eae502283889334
|
||||
[#4528](https://github.com/angular/angular.js/issues/4528), and
|
||||
[#4649](https://github.com/angular/angular.js/issues/4649)
|
||||
|
||||
### Underscore-prefixed/suffixed properties are non-bindable
|
||||
## Underscore-prefixed/suffixed properties are non-bindable
|
||||
|
||||
<div class="alert alert-info">
|
||||
<p>**Reverted**: This breaking change has been reverted in 1.2.1, and so can be ignored if you're using **version 1.2.1 or higher**</p>
|
||||
@@ -1855,7 +1626,7 @@ are actually needed by the expressions.
|
||||
See [3d6a89e8](https://github.com/angular/angular.js/commit/3d6a89e8888b14ae5cb5640464e12b7811853c7e).
|
||||
|
||||
|
||||
### You cannot bind to select[multiple]
|
||||
## You cannot bind to select[multiple]
|
||||
|
||||
Switching between `select[single]` and `select[multiple]` has always been odd due to browser quirks.
|
||||
This feature never worked with two-way data-binding so it's not expected that anyone is using it.
|
||||
@@ -1865,7 +1636,7 @@ If you are interested in properly adding this feature, please submit a pull requ
|
||||
See [d87fa004](https://github.com/angular/angular.js/commit/d87fa0042375b025b98c40bff05e5f42c00af114).
|
||||
|
||||
|
||||
### Uncommon region-specific local files were removed from i18n
|
||||
## Uncommon region-specific local files were removed from i18n
|
||||
|
||||
AngularJS uses the Google Closure library's locale files. The following locales were removed from
|
||||
Closure, so Angular is not able to continue to support them:
|
||||
@@ -1881,7 +1652,7 @@ load and use your copy of the locale file provided that you maintain it yourself
|
||||
|
||||
See [6382e21f](https://github.com/angular/angular.js/commit/6382e21fb28541a2484ac1a241d41cf9fbbe9d2c).
|
||||
|
||||
### Services can now return functions
|
||||
## Services can now return functions
|
||||
|
||||
Previously, the service constructor only returned objects regardless of whether a function was returned.
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ the `$digest` phase. This delay is desirable, since it coalesces multiple model
|
||||
|
||||
2. **Watcher registration**
|
||||
|
||||
During template linking directives register {@link
|
||||
During template linking, directives register {@link
|
||||
ng.$rootScope.Scope#$watch watches} on the scope. These watches will be
|
||||
used to propagate model values to the DOM.
|
||||
|
||||
|
||||
@@ -9,27 +9,6 @@ This document explains some of AngularJS's security features and best practices
|
||||
keep in mind as you build your application.
|
||||
|
||||
|
||||
## Reporting a security issue
|
||||
|
||||
Email us at [security@angularjs.org](mailto:security@angularjs.org) to report any potential
|
||||
security issues in AngularJS.
|
||||
|
||||
Please keep in mind the points below about Angular's expression language.
|
||||
|
||||
|
||||
## Use the latest AngularJS possible
|
||||
|
||||
Like any software library, it is critical to keep AngularJS up to date. Please track the
|
||||
[CHANGELOG](https://github.com/angular/angular.js/blob/master/CHANGELOG.md) and make sure you are aware
|
||||
of upcoming security patches and other updates.
|
||||
|
||||
Be ready to update rapidly when new security-centric patches are available.
|
||||
|
||||
Those that stray from Angular standards (such as modifying Angular's core) may have difficulty updating,
|
||||
so keeping to AngularJS standards is not just a functionality issue, it's also critical in order to
|
||||
facilitate rapid security updates.
|
||||
|
||||
|
||||
## Expression Sandboxing
|
||||
|
||||
AngularJS's expressions are sandboxed not for security reasons, but instead to maintain a proper
|
||||
@@ -46,8 +25,7 @@ But if an attacker can change arbitrary HTML templates, there's nothing stopping
|
||||
<script>somethingEvil();</script>
|
||||
```
|
||||
|
||||
**It's better to design your application in such a way that users cannot change client-side templates.**
|
||||
|
||||
It's better to design your application in such a way that users cannot change client-side templates.
|
||||
For instance:
|
||||
|
||||
* Do not mix client and server templates
|
||||
@@ -55,8 +33,7 @@ For instance:
|
||||
* Do not run user input through `$scope.$eval`
|
||||
* Consider using {@link ng.directive:ngCsp CSP} (but don't rely only on CSP)
|
||||
|
||||
|
||||
### Mixing client-side and server-side templates
|
||||
## Mixing client-side and server-side templates
|
||||
|
||||
In general, we recommend against this because it can create unintended XSS vectors.
|
||||
|
||||
@@ -64,62 +41,20 @@ However, it's ok to mix server-side templating in the bootstrap template (`index
|
||||
as user input cannot be used on the server to output html that would then be processed by Angular
|
||||
in a way that would allow for arbitrary code execution.
|
||||
|
||||
**For instance, you can use server-side templating to dynamically generate CSS, URLs, etc, but not
|
||||
for generating templates that are bootstrapped/compiled by Angular.**
|
||||
For instance, you can use server-side templating to dynamically generate CSS, URLs, etc, but not
|
||||
for generating templates that are bootstrapped/compiled by Angular.
|
||||
|
||||
|
||||
## HTTP Requests
|
||||
## Reporting a security issue
|
||||
|
||||
Whenever your application makes requests to a server there are potential security issues that need
|
||||
to be blocked. Both server and the client must cooperate in order to eliminate these threats.
|
||||
Angular comes pre-configured with strategies that address these issues, but for this to work backend
|
||||
server cooperation is required.
|
||||
Email us at [security@angularjs.org](mailto:security@angularjs.org) to report any potential
|
||||
security issues in AngularJS.
|
||||
|
||||
Please keep in mind the above points about Angular's expression language.
|
||||
|
||||
### Cross Site Request Forgery (XSRF/CSRF)
|
||||
|
||||
Protection from XSRF is provided by using the double-submit cookie defense pattern.
|
||||
For more information please visit {@link $http#cross-site-request-forgery-xsrf-protection XSRF protection}.
|
||||
|
||||
### JSON Hijacking Protection
|
||||
|
||||
Protection from JSON Hijacking is provided if the server prefixes all JSON requests with following string `")]}',\n"`.
|
||||
Angular will automatically strip the prefix before processing it as JSON.
|
||||
For more information please visit {@link $http#json-vulnerability-protection JSON Hijacking Protection}.
|
||||
|
||||
|
||||
## Strict Contextual Escaping
|
||||
|
||||
Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain contexts to require
|
||||
a value that is marked as safe to use for that context.
|
||||
|
||||
This mode is implemented by the {@link $sce} service and various core directives.
|
||||
|
||||
One example of such a context is rendering arbitrary content via the {@link ngBindHtml} directive. If the content is
|
||||
provided by a user there is a chance of Cross Site Scripting (XSS) attacks. The {@link ngBindHtml} directive will not
|
||||
render content that is not marked as safe by {@link $sce}. The {@link ngSanitize} module can be used to clean such
|
||||
user provided content and mark the content as safe.
|
||||
|
||||
**Be aware that marking untrusted data as safe via calls to {@link $sce#trustAsHtml `$sce.trustAsHtml`}, etc is
|
||||
dangerous and will lead to Cross Site Scripting exploits.**
|
||||
|
||||
For more information please visit {@link $sce} and {@link ngSanitize.$sanitize}.
|
||||
|
||||
## Using Local Caches
|
||||
|
||||
There are various places that the browser can store (or cache) data. Within Angular there are objects created by
|
||||
the {@link $cacheFactory}. These objects, such as {@link $templateCache} are used to store and retrieve data,
|
||||
primarily used by {@link $http} and the {@link script} directive to cache templates and other data.
|
||||
|
||||
Similarly the browser itself offers `localStorage` and `sessionStorage` objects for caching data.
|
||||
|
||||
**Attackers with local access can retrieve sensitive data from this cache even when users are not authenticated.**
|
||||
|
||||
For instance in a long running Single Page Application (SPA), one user may "log out", but then another user
|
||||
may access the application without refreshing, in which case all the cached data is still available.
|
||||
|
||||
For more information please visit [Web Storage Security](https://www.whitehatsec.com/blog/web-storage-security/).
|
||||
|
||||
## See also
|
||||
|
||||
* {@link ng.directive:ngCsp Content Security Policy}
|
||||
* {@link ng.$sce Strict Contextual Escaping}
|
||||
* {@link ngSanitize.$sanitize $sanitize}
|
||||
|
||||
@@ -43,7 +43,7 @@ subsystem takes care of the rest.
|
||||
<file name="script.js">
|
||||
angular.
|
||||
module('myServiceModule', []).
|
||||
controller('MyController', ['$scope', 'notify', function ($scope, notify) {
|
||||
controller('MyController', ['$scope','notify', function ($scope, notify) {
|
||||
$scope.callNotify = function(msg) {
|
||||
notify(msg);
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
JavaScript is a dynamically typed language which comes with great power of expression, but it also
|
||||
comes with almost no help from the compiler. For this reason we feel very strongly that any code
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
Angular which make testing your Angular applications easy. With Angular, there is no excuse for not testing.
|
||||
Angular which makes testing your Angular applications easy. So there is no excuse for not testing.
|
||||
|
||||
## Separation of Concerns
|
||||
|
||||
@@ -20,13 +20,13 @@ related pieces such as the DOM elements, or making any XHR calls to fetch the da
|
||||
|
||||
While this may seem obvious it can be very difficult to call an individual function on a
|
||||
typical project. The reason is that the developers often mix concerns resulting in a
|
||||
piece of code which does everything. It makes an XHR request, it sorts the response data, and then it
|
||||
piece of code which does everything. It makes an XHR request, it sorts the response data and then it
|
||||
manipulates the DOM.
|
||||
|
||||
With Angular, we try to make it easy for you to do the right thing. For your XHR requests, we
|
||||
provide dependency injection, so your requests can be simulated. For the DOM, we abstract it, so you can
|
||||
test your model without having to manipulate the DOM directly. Your tests can then
|
||||
assert that the data has been sorted without having to create or look at the state of the DOM or to
|
||||
With Angular we try to make it easy for you to do the right thing, and so we
|
||||
provide dependency injection for your XHR requests, which can be mocked, and we provide abstractions which
|
||||
allow you to test your model without having to resort to manipulating the DOM. The test can then
|
||||
assert that the data has been sorted without having to create or look at the state of the DOM or
|
||||
wait for any XHR requests to return data. The individual sort function can be tested in isolation.
|
||||
|
||||
## With great power comes great responsibility
|
||||
@@ -430,50 +430,5 @@ If your directive uses `templateUrl`, consider using
|
||||
to pre-compile HTML templates and thus avoid having to load them over HTTP during test execution.
|
||||
Otherwise you may run into issues if the test directory hierarchy differs from the application's.
|
||||
|
||||
## Testing Promises
|
||||
|
||||
When testing promises, it's important to know that the resolution of promises is tied to the {@link ng.$rootScope.Scope#$digest digest cycle}.
|
||||
That means a promise's `then`, `catch` and `finally` callback functions are only called after a digest has run.
|
||||
In tests, you can trigger a digest by calling a scope's {@link ng.$rootScope.Scope#$apply `$apply` function}.
|
||||
If you don't have a scope in your test, you can inject the {@link ng.$rootScope $rootScope} and call `$apply` on it.
|
||||
There is also an example of testing promises in the {@link ng.$q#testing `$q` service documentation}.
|
||||
|
||||
## Using `beforeAll()`
|
||||
|
||||
Jasmine's `beforeAll()` and mocha's `before()` hooks are often useful for sharing test setup - either to reduce test run-time or simply to make for more focussed test cases.
|
||||
|
||||
By default, ngMock will create an injector per test case to ensure your tests do not affect each other. However, if we want to use `beforeAll()`, ngMock will have to create the injector before any test cases are run, and share that injector through all the cases for that `describe`. That is where {@link angular.mock.module.sharedInjector module.sharedInjector()} comes in. When it's called within a `describe` block, a single injector is shared between all hooks and test cases run in that block.
|
||||
|
||||
In the example below we are testing a service that takes a long time to generate its answer. To avoid having all of the assertions we want to write in a single test case, {@link angular.mock.module.sharedInjector module.sharedInjector()} and Jasmine's `beforeAll()` are used to run the service only once. The test cases then all make assertions about the properties added to the service instance.
|
||||
|
||||
```javascript
|
||||
describe("Deep Thought", function() {
|
||||
|
||||
module.sharedInjector();
|
||||
|
||||
beforeAll(module("UltimateQuestion"));
|
||||
|
||||
beforeAll(inject(function(DeepThought) {
|
||||
expect(DeepThought.answer).toBeUndefined();
|
||||
DeepThought.generateAnswer();
|
||||
}));
|
||||
|
||||
it("has calculated the answer correctly", inject(function(DeepThought) {
|
||||
// Because of sharedInjector, we have access to the instance of the DeepThought service
|
||||
// that was provided to the beforeAll() hook. Therefore we can test the generated answer
|
||||
expect(DeepThought.answer).toBe(42);
|
||||
}));
|
||||
|
||||
it("has calculated the answer within the expected time", inject(function(DeepThought) {
|
||||
expect(DeepThought.runTimeMillennia).toBeLessThan(8000);
|
||||
}));
|
||||
|
||||
it("has double checked the answer", inject(function(DeepThought) {
|
||||
expect(DeepThought.absolutelySureItIsTheRightAnswer).toBe(true);
|
||||
}));
|
||||
|
||||
});
|
||||
```
|
||||
|
||||
## Sample project
|
||||
See the [angular-seed](https://github.com/angular/angular-seed) project for an example.
|
||||
|
||||
@@ -246,15 +246,6 @@ npm run update-webdriver
|
||||
|
||||
*(You should only need to do this once.)*
|
||||
|
||||
You will need to have Java present on your dev machine to allow the Selenium standalone to be started.
|
||||
Check if you already have java installed by opening a terminal/command line window and typing
|
||||
'''
|
||||
java -version
|
||||
'''
|
||||
If java is already installed and exists in the PATH then you will be shown the version installed,
|
||||
if, however you receive a message that "java is not recognized as an internal command or external
|
||||
command" you will need to install [java].
|
||||
|
||||
Since Protractor works by interacting with a running application, we need to start our web server:
|
||||
|
||||
```
|
||||
@@ -289,4 +280,3 @@ Now that you have set up your local machine, let's get started with the tutorial
|
||||
[bower]: http://bower.io/
|
||||
[http-server]: https://github.com/nodeapps/http-server
|
||||
[karma]: https://github.com/karma-runner/karma
|
||||
[java]: https://www.java.com/en/download/help/download_options.xml
|
||||
|
||||
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 30 KiB |
@@ -20,7 +20,7 @@
|
||||
* using the --for_closure flag.
|
||||
* File generated from CLDR ver. 27.0.1
|
||||
*
|
||||
* This file covers those locales that are not covered in
|
||||
* This file coveres those locales that are not covered in
|
||||
* "numberformatsymbols.js".
|
||||
*
|
||||
* Before checkin, this file could have been manually edited. This is
|
||||
|
||||
@@ -272,7 +272,7 @@ describe("serializeContent", function() {
|
||||
it("should not transform arrays into objects", function() {
|
||||
var serializedContent = closureI18nExtractor.serializeContent(newTestLocaleInfo().fr_CA);
|
||||
var deserializedLocale = eval("(" + serializedContent + ")");
|
||||
expect(deserializedLocale.DATETIME_FORMATS.MONTH.length).not.toBeUndefined();
|
||||
expect(deserializedLocale.DATETIME_FORMATS.MONTH.length).not.toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ var PATTERN_SEP = ';',
|
||||
DIGIT = '#';
|
||||
|
||||
/**
|
||||
* main function for parser
|
||||
* main funciton for parser
|
||||
* @param str {string} pattern to be parsed (e.g. #,##0.###).
|
||||
*/
|
||||
function parsePattern(pattern) {
|
||||
|
||||
@@ -37,25 +37,19 @@ module.exports = function(config, specificOptions) {
|
||||
'SL_Chrome': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
version: '47'
|
||||
version: '43'
|
||||
},
|
||||
'SL_Firefox': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: '43'
|
||||
version: '39'
|
||||
},
|
||||
'SL_Safari_8': {
|
||||
'SL_Safari': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.10',
|
||||
version: '8'
|
||||
},
|
||||
'SL_Safari_9': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.11',
|
||||
version: '9'
|
||||
},
|
||||
'SL_IE_9': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
|
||||
@@ -287,7 +287,7 @@ module.exports = {
|
||||
rewrite: function(){
|
||||
return function(req, res, next){
|
||||
var REWRITE = /\/(guide|api|cookbook|misc|tutorial|error).*$/,
|
||||
IGNORED = /(\.(css|js|png|jpg|gif|svg)$|partials\/.*\.html$)/,
|
||||
IGNORED = /(\.(css|js|png|jpg|gif)$|partials\/.*\.html$)/,
|
||||
match;
|
||||
|
||||
if (!IGNORED.test(req.url) && (match = req.url.match(REWRITE))) {
|
||||
|
||||
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* HTML Parser By John Resig (ejohn.org)
|
||||
* Original code by Erik Arvidsson, Mozilla Public License
|
||||
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
|
||||
*
|
||||
* // Use like so:
|
||||
* htmlParser(htmlString, {
|
||||
* start: function(tag, attrs, unary) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* });
|
||||
*
|
||||
* // or to get an XML string:
|
||||
* HTMLtoXML(htmlString);
|
||||
*
|
||||
* // or to get an XML DOM Document
|
||||
* HTMLtoDOM(htmlString);
|
||||
*
|
||||
* // or to inject into an existing document/DOM node
|
||||
* HTMLtoDOM(htmlString, document);
|
||||
* HTMLtoDOM(htmlString, document.body);
|
||||
*
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
var startTag = /^<(\w+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/,
|
||||
endTag = /^<\/(\w+)[^>]*>/,
|
||||
attr = /(\w+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g;
|
||||
|
||||
// Empty Elements - HTML 4.01
|
||||
var empty = makeMap("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed");
|
||||
|
||||
// Block Elements - HTML 4.01
|
||||
var block = makeMap("address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul");
|
||||
|
||||
// Inline Elements - HTML 4.01
|
||||
var inline = makeMap("a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var");
|
||||
|
||||
// Elements that you can, intentionally, leave open
|
||||
// (and which close themselves)
|
||||
var closeSelf = makeMap("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");
|
||||
|
||||
// Attributes that have their values filled in disabled="disabled"
|
||||
var fillAttrs = makeMap("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected");
|
||||
|
||||
// Special Elements (can contain anything)
|
||||
var special = makeMap("script,style");
|
||||
|
||||
var htmlParser = this.htmlParser = function( html, handler ) {
|
||||
var index, chars, match, stack = [], last = html;
|
||||
stack.last = function(){
|
||||
return this[ this.length - 1 ];
|
||||
};
|
||||
|
||||
while ( html ) {
|
||||
chars = true;
|
||||
|
||||
// Make sure we're not in a script or style element
|
||||
if ( !stack.last() || !special[ stack.last() ] ) {
|
||||
|
||||
// Comment
|
||||
if ( html.indexOf("<!--") == 0 ) {
|
||||
index = html.indexOf("-->");
|
||||
|
||||
if ( index >= 0 ) {
|
||||
if ( handler.comment )
|
||||
handler.comment( html.substring( 4, index ) );
|
||||
html = html.substring( index + 3 );
|
||||
chars = false;
|
||||
}
|
||||
|
||||
// end tag
|
||||
} else if ( html.indexOf("</") == 0 ) {
|
||||
match = html.match( endTag );
|
||||
|
||||
if ( match ) {
|
||||
html = html.substring( match[0].length );
|
||||
match[0].replace( endTag, parseEndTag );
|
||||
chars = false;
|
||||
}
|
||||
|
||||
// start tag
|
||||
} else if ( html.indexOf("<") == 0 ) {
|
||||
match = html.match( startTag );
|
||||
|
||||
if ( match ) {
|
||||
html = html.substring( match[0].length );
|
||||
match[0].replace( startTag, parseStartTag );
|
||||
chars = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( chars ) {
|
||||
index = html.indexOf("<");
|
||||
|
||||
var text = index < 0 ? html : html.substring( 0, index );
|
||||
html = index < 0 ? "" : html.substring( index );
|
||||
|
||||
if ( handler.chars )
|
||||
handler.chars( text );
|
||||
}
|
||||
|
||||
} else {
|
||||
html = html.replace(new RegExp("(.*)<\/" + stack.last() + "[^>]*>"), function(all, text){
|
||||
text = text.replace(/<!--(.*?)-->/g, "$1")
|
||||
.replace(/<!\[CDATA\[(.*?)]]>/g, "$1");
|
||||
|
||||
if ( handler.chars )
|
||||
handler.chars( text );
|
||||
|
||||
return "";
|
||||
});
|
||||
|
||||
parseEndTag( "", stack.last() );
|
||||
}
|
||||
|
||||
if ( html == last )
|
||||
throw "Parse Error: " + html;
|
||||
last = html;
|
||||
}
|
||||
|
||||
// Clean up any remaining tags
|
||||
parseEndTag();
|
||||
|
||||
function parseStartTag( tag, tagName, rest, unary ) {
|
||||
if ( block[ tagName ] ) {
|
||||
while ( stack.last() && inline[ stack.last() ] ) {
|
||||
parseEndTag( "", stack.last() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( closeSelf[ tagName ] && stack.last() == tagName ) {
|
||||
parseEndTag( "", tagName );
|
||||
}
|
||||
|
||||
unary = empty[ tagName ] || !!unary;
|
||||
|
||||
if ( !unary )
|
||||
stack.push( tagName );
|
||||
|
||||
if ( handler.start ) {
|
||||
var attrs = [];
|
||||
|
||||
rest.replace(attr, function(match, name) {
|
||||
var value = arguments[2] ? arguments[2] :
|
||||
arguments[3] ? arguments[3] :
|
||||
arguments[4] ? arguments[4] :
|
||||
fillAttrs[name] ? name : "";
|
||||
|
||||
attrs.push({
|
||||
name: name,
|
||||
value: value,
|
||||
escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') //"
|
||||
});
|
||||
});
|
||||
|
||||
if ( handler.start )
|
||||
handler.start( tagName, attrs, unary );
|
||||
}
|
||||
}
|
||||
|
||||
function parseEndTag( tag, tagName ) {
|
||||
// If no tag name is provided, clean shop
|
||||
if ( !tagName )
|
||||
var pos = 0;
|
||||
|
||||
// Find the closest opened tag of the same type
|
||||
else
|
||||
for ( var pos = stack.length - 1; pos >= 0; pos-- )
|
||||
if ( stack[ pos ] == tagName )
|
||||
break;
|
||||
|
||||
if ( pos >= 0 ) {
|
||||
// Close all the open elements, up the stack
|
||||
for ( var i = stack.length - 1; i >= pos; i-- )
|
||||
if ( handler.end )
|
||||
handler.end( stack[ i ] );
|
||||
|
||||
// Remove the open elements from the stack
|
||||
stack.length = pos;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.HTMLtoXML = function( html ) {
|
||||
var results = "";
|
||||
|
||||
htmlParser(html, {
|
||||
start: function( tag, attrs, unary ) {
|
||||
results += "<" + tag;
|
||||
|
||||
for ( var i = 0; i < attrs.length; i++ )
|
||||
results += " " + attrs[i].name + '="' + attrs[i].escaped + '"';
|
||||
|
||||
results += (unary ? "/" : "") + ">";
|
||||
},
|
||||
end: function( tag ) {
|
||||
results += "</" + tag + ">";
|
||||
},
|
||||
chars: function( text ) {
|
||||
results += text;
|
||||
},
|
||||
comment: function( text ) {
|
||||
results += "<!--" + text + "-->";
|
||||
}
|
||||
});
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
this.HTMLtoDOM = function( html, doc ) {
|
||||
// There can be only one of these elements
|
||||
var one = makeMap("html,head,body,title");
|
||||
|
||||
// Enforce a structure for the document
|
||||
var structure = {
|
||||
link: "head",
|
||||
base: "head"
|
||||
};
|
||||
|
||||
if ( !doc ) {
|
||||
if ( typeof DOMDocument != "undefined" )
|
||||
doc = new DOMDocument();
|
||||
else if ( typeof document != "undefined" && document.implementation && document.implementation.createDocument )
|
||||
doc = document.implementation.createDocument("", "", null);
|
||||
else if ( typeof ActiveX != "undefined" )
|
||||
doc = new ActiveXObject("Msxml.DOMDocument");
|
||||
|
||||
} else
|
||||
doc = doc.ownerDocument ||
|
||||
doc.getOwnerDocument && doc.getOwnerDocument() ||
|
||||
doc;
|
||||
|
||||
var elems = [],
|
||||
documentElement = doc.documentElement ||
|
||||
doc.getDocumentElement && doc.getDocumentElement();
|
||||
|
||||
// If we're dealing with an empty document then we
|
||||
// need to pre-populate it with the HTML document structure
|
||||
if ( !documentElement && doc.createElement ) (function(){
|
||||
var html = doc.createElement("html");
|
||||
var head = doc.createElement("head");
|
||||
head.appendChild( doc.createElement("title") );
|
||||
html.appendChild( head );
|
||||
html.appendChild( doc.createElement("body") );
|
||||
doc.appendChild( html );
|
||||
})();
|
||||
|
||||
// Find all the unique elements
|
||||
if ( doc.getElementsByTagName )
|
||||
for ( var i in one )
|
||||
one[ i ] = doc.getElementsByTagName( i )[0];
|
||||
|
||||
// If we're working with a document, inject contents into
|
||||
// the body element
|
||||
var curParentNode = one.body;
|
||||
|
||||
htmlParser( html, {
|
||||
start: function( tagName, attrs, unary ) {
|
||||
// If it's a pre-built element, then we can ignore
|
||||
// its construction
|
||||
if ( one[ tagName ] ) {
|
||||
curParentNode = one[ tagName ];
|
||||
return;
|
||||
}
|
||||
|
||||
var elem = doc.createElement( tagName );
|
||||
|
||||
for ( var attr in attrs )
|
||||
elem.setAttribute( attrs[ attr ].name, attrs[ attr ].value );
|
||||
|
||||
if ( structure[ tagName ] && typeof one[ structure[ tagName ] ] != "boolean" )
|
||||
one[ structure[ tagName ] ].appendChild( elem );
|
||||
|
||||
else if ( curParentNode && curParentNode.appendChild )
|
||||
curParentNode.appendChild( elem );
|
||||
|
||||
if ( !unary ) {
|
||||
elems.push( elem );
|
||||
curParentNode = elem;
|
||||
}
|
||||
},
|
||||
end: function( tag ) {
|
||||
elems.length -= 1;
|
||||
|
||||
// Init the new parentNode
|
||||
curParentNode = elems[ elems.length - 1 ];
|
||||
},
|
||||
chars: function( text ) {
|
||||
curParentNode.appendChild( doc.createTextNode( text ) );
|
||||
},
|
||||
comment: function( text ) {
|
||||
// create comment node
|
||||
}
|
||||
});
|
||||
|
||||
return doc;
|
||||
};
|
||||
|
||||
function makeMap(str){
|
||||
var obj = {}, items = str.split(",");
|
||||
for ( var i = 0; i < items.length; i++ )
|
||||
obj[ items[i] ] = true;
|
||||
return obj;
|
||||
}
|
||||
})();
|
||||
@@ -11,7 +11,8 @@ 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
|
||||
CONNECT_URL="https://saucelabs.com/downloads/sc-4.3.13-linux.tar.gz"
|
||||
|
||||
CONNECT_URL="https://saucelabs.com/downloads/sc-4.3.7-linux.tar.gz"
|
||||
CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
|
||||
CONNECT_DOWNLOAD="sc-4.3.7-linux.tar.gz"
|
||||
|
||||
|
||||
@@ -178,7 +178,7 @@ var getSnapshotVersion = function() {
|
||||
// last release was a non beta release. Increment the patch level to
|
||||
// indicate the next release that we will be doing.
|
||||
// E.g. last release was 1.3.0, then the snapshot will be
|
||||
// 1.3.1-build.1, which is lesser than 1.3.1 according to the semver!
|
||||
// 1.3.1-build.1, which is lesser than 1.3.1 accorind the semver!
|
||||
|
||||
// If the last release was a beta release we don't update the
|
||||
// beta number by purpose, as otherwise the semver comparison
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"license": "MIT",
|
||||
"branchVersion": "1.5.x",
|
||||
"branchPattern": "1.5.*",
|
||||
"distTag": "latest",
|
||||
"branchVersion": "1.4.x",
|
||||
"branchPattern": "1.4.*",
|
||||
"distTag": "previous_1_4",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": "<5",
|
||||
"node": "~0.10",
|
||||
"npm": "~2.5"
|
||||
},
|
||||
"engineStrict": true,
|
||||
@@ -27,7 +27,7 @@
|
||||
"canonical-path": "0.0.2",
|
||||
"cheerio": "^0.17.0",
|
||||
"commitizen": "^2.3.0",
|
||||
"cz-conventional-changelog": "1.1.4",
|
||||
"cz-conventional-changelog": "^1.1.4",
|
||||
"dgeni": "^0.4.0",
|
||||
"dgeni-packages": "^0.11.0",
|
||||
"event-stream": "~3.1.0",
|
||||
@@ -41,7 +41,7 @@
|
||||
"grunt-contrib-jshint": "~0.10.0",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
||||
"grunt-jscs": "^2.1.0",
|
||||
"grunt-jscs": "~1.2.0",
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"grunt-shell": "~1.1.1",
|
||||
"gulp": "~3.8.0",
|
||||
@@ -52,7 +52,6 @@
|
||||
"gulp-sourcemaps": "^1.2.2",
|
||||
"gulp-uglify": "^1.0.1",
|
||||
"gulp-util": "^3.0.1",
|
||||
"jasmine-core": "^2.4.0",
|
||||
"jasmine-node": "^2.0.0",
|
||||
"jasmine-reporters": "~1.0.1",
|
||||
"jshint-stylish": "~1.0.0",
|
||||
@@ -60,7 +59,7 @@
|
||||
"karma-browserstack-launcher": "^0.1.8",
|
||||
"karma-chrome-launcher": "^0.2.2",
|
||||
"karma-firefox-launcher": "^0.1.7",
|
||||
"karma-jasmine": "^0.3.7",
|
||||
"karma-jasmine": "^0.1.6",
|
||||
"karma-junit-reporter": "^0.3.8",
|
||||
"karma-ng-scenario": "^0.1.0",
|
||||
"karma-sauce-launcher": "^0.3.0",
|
||||
|
||||
@@ -11,7 +11,7 @@ elif [ $JOB = "unit" ]; then
|
||||
if [ "$BROWSER_PROVIDER" == "browserstack" ]; then
|
||||
BROWSERS="BS_Chrome,BS_Safari,BS_Firefox,BS_IE_9,BS_IE_10,BS_IE_11,BS_iOS"
|
||||
else
|
||||
BROWSERS="SL_Chrome,SL_Firefox,SL_Safari_8,SL_Safari_9,SL_IE_9,SL_IE_10,SL_IE_11,SL_iOS"
|
||||
BROWSERS="SL_Chrome,SL_Safari,SL_Firefox,SL_IE_9,SL_IE_10,SL_IE_11,SL_iOS"
|
||||
fi
|
||||
|
||||
grunt test:promises-aplus
|
||||
|
||||
@@ -94,6 +94,8 @@
|
||||
"VALIDITY_STATE_PROPERTY": false,
|
||||
"reloadWithDebugInfo": false,
|
||||
|
||||
"skipDestroyOnNextJQueryCleanData": true,
|
||||
|
||||
"NODE_TYPE_ELEMENT": false,
|
||||
"NODE_TYPE_ATTRIBUTE": false,
|
||||
"NODE_TYPE_TEXT": false,
|
||||
|
||||
@@ -119,9 +119,29 @@ var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
|
||||
// This is used so that it's possible for internal tests to create mock ValidityStates.
|
||||
var VALIDITY_STATE_PROPERTY = 'validity';
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.lowercase
|
||||
* @module ng
|
||||
* @kind function
|
||||
*
|
||||
* @description Converts the specified string to lowercase.
|
||||
* @param {string} string String to be converted to lowercase.
|
||||
* @returns {string} Lowercased string.
|
||||
*/
|
||||
var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
|
||||
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.uppercase
|
||||
* @module ng
|
||||
* @kind function
|
||||
*
|
||||
* @description Converts the specified string to uppercase.
|
||||
* @param {string} string String to be converted to uppercase.
|
||||
* @returns {string} Uppercased string.
|
||||
*/
|
||||
var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
|
||||
|
||||
|
||||
@@ -141,7 +161,7 @@ var manualUppercase = function(s) {
|
||||
|
||||
// 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
|
||||
// with correct but slower alternatives.
|
||||
if ('i' !== 'I'.toLowerCase()) {
|
||||
lowercase = manualLowercase;
|
||||
uppercase = manualUppercase;
|
||||
@@ -184,7 +204,7 @@ function isArrayLike(obj) {
|
||||
|
||||
// arrays, strings and jQuery/jqLite objects are array like
|
||||
// * jqLite is either the jQuery or jqLite constructor function
|
||||
// * we have to check the existence of jqLite first as this method is called
|
||||
// * we have to check the existance of jqLite first as this method is called
|
||||
// via the forEach method when constructing the jqLite object in the first place
|
||||
if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
|
||||
|
||||
@@ -446,18 +466,28 @@ noop.$inject = [];
|
||||
* functional style.
|
||||
*
|
||||
```js
|
||||
function transformer(transformationFn, value) {
|
||||
return (transformationFn || angular.identity)(value);
|
||||
};
|
||||
function transformer(transformationFn, value) {
|
||||
return (transformationFn || angular.identity)(value);
|
||||
};
|
||||
|
||||
// E.g.
|
||||
function getResult(fn, input) {
|
||||
return (fn || angular.identity)(input);
|
||||
};
|
||||
|
||||
getResult(function(n) { return n * 2; }, 21); // returns 42
|
||||
getResult(null, 21); // returns 21
|
||||
getResult(undefined, 21); // returns 21
|
||||
```
|
||||
* @param {*} value to be returned.
|
||||
* @returns {*} the value passed in.
|
||||
*
|
||||
* @param {*} value to be returned.
|
||||
* @returns {*} the value passed in.
|
||||
*/
|
||||
function identity($) {return $;}
|
||||
identity.$inject = [];
|
||||
|
||||
|
||||
function valueFn(value) {return function valueRef() {return value;};}
|
||||
function valueFn(value) {return function() {return value;};}
|
||||
|
||||
function hasCustomToString(obj) {
|
||||
return isFunction(obj.toString) && obj.toString !== toString;
|
||||
@@ -664,10 +694,6 @@ function isTypedArray(value) {
|
||||
return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
|
||||
}
|
||||
|
||||
function isArrayBuffer(obj) {
|
||||
return toString.call(obj) === '[object ArrayBuffer]';
|
||||
}
|
||||
|
||||
|
||||
var trim = function(value) {
|
||||
return isString(value) ? value.trim() : value;
|
||||
@@ -792,7 +818,7 @@ function copy(source, destination) {
|
||||
var stackDest = [];
|
||||
|
||||
if (destination) {
|
||||
if (isTypedArray(destination) || isArrayBuffer(destination)) {
|
||||
if (isTypedArray(destination)) {
|
||||
throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
|
||||
}
|
||||
if (source === destination) {
|
||||
@@ -819,7 +845,7 @@ function copy(source, destination) {
|
||||
|
||||
function copyRecurse(source, destination) {
|
||||
var h = destination.$$hashKey;
|
||||
var key;
|
||||
var result, key;
|
||||
if (isArray(source)) {
|
||||
for (var i = 0, ii = source.length; i < ii; i++) {
|
||||
destination.push(copyElement(source[i]));
|
||||
@@ -866,10 +892,24 @@ function copy(source, destination) {
|
||||
}
|
||||
|
||||
var needsRecurse = false;
|
||||
var destination = copyType(source);
|
||||
var destination;
|
||||
|
||||
if (destination === undefined) {
|
||||
destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
|
||||
if (isArray(source)) {
|
||||
destination = [];
|
||||
needsRecurse = true;
|
||||
} else if (isTypedArray(source)) {
|
||||
destination = new source.constructor(source);
|
||||
} else if (isDate(source)) {
|
||||
destination = new Date(source.getTime());
|
||||
} else if (isRegExp(source)) {
|
||||
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
|
||||
destination.lastIndex = source.lastIndex;
|
||||
} else if (isBlob(source)) {
|
||||
destination = new source.constructor([source], {type: source.type});
|
||||
} else if (isFunction(source.cloneNode)) {
|
||||
destination = source.cloneNode(true);
|
||||
} else {
|
||||
destination = Object.create(getPrototypeOf(source));
|
||||
needsRecurse = true;
|
||||
}
|
||||
|
||||
@@ -880,48 +920,6 @@ function copy(source, destination) {
|
||||
? copyRecurse(source, destination)
|
||||
: destination;
|
||||
}
|
||||
|
||||
function copyType(source) {
|
||||
switch (toString.call(source)) {
|
||||
case '[object Int8Array]':
|
||||
case '[object Int16Array]':
|
||||
case '[object Int32Array]':
|
||||
case '[object Float32Array]':
|
||||
case '[object Float64Array]':
|
||||
case '[object Uint8Array]':
|
||||
case '[object Uint8ClampedArray]':
|
||||
case '[object Uint16Array]':
|
||||
case '[object Uint32Array]':
|
||||
return new source.constructor(copyElement(source.buffer));
|
||||
|
||||
case '[object ArrayBuffer]':
|
||||
//Support: IE10
|
||||
if (!source.slice) {
|
||||
var copied = new ArrayBuffer(source.byteLength);
|
||||
new Uint8Array(copied).set(new Uint8Array(source));
|
||||
return copied;
|
||||
}
|
||||
return source.slice(0);
|
||||
|
||||
case '[object Boolean]':
|
||||
case '[object Number]':
|
||||
case '[object String]':
|
||||
case '[object Date]':
|
||||
return new source.constructor(source.valueOf());
|
||||
|
||||
case '[object RegExp]':
|
||||
var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
|
||||
re.lastIndex = source.lastIndex;
|
||||
return re;
|
||||
|
||||
case '[object Blob]':
|
||||
return new source.constructor([source], {type: source.type});
|
||||
}
|
||||
|
||||
if (isFunction(source.cloneNode)) {
|
||||
return source.cloneNode(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -984,37 +982,38 @@ function equals(o1, o2) {
|
||||
if (o1 === null || o2 === null) return false;
|
||||
if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
|
||||
var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
|
||||
if (t1 == t2 && t1 == 'object') {
|
||||
if (isArray(o1)) {
|
||||
if (!isArray(o2)) return false;
|
||||
if ((length = o1.length) == o2.length) {
|
||||
for (key = 0; key < length; key++) {
|
||||
if (t1 == t2) {
|
||||
if (t1 == 'object') {
|
||||
if (isArray(o1)) {
|
||||
if (!isArray(o2)) return false;
|
||||
if ((length = o1.length) == o2.length) {
|
||||
for (key = 0; key < length; key++) {
|
||||
if (!equals(o1[key], o2[key])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else if (isDate(o1)) {
|
||||
if (!isDate(o2)) return false;
|
||||
return equals(o1.getTime(), o2.getTime());
|
||||
} else if (isRegExp(o1)) {
|
||||
return isRegExp(o2) ? o1.toString() == o2.toString() : false;
|
||||
} else {
|
||||
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
|
||||
isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
|
||||
keySet = createMap();
|
||||
for (key in o1) {
|
||||
if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
|
||||
if (!equals(o1[key], o2[key])) return false;
|
||||
keySet[key] = true;
|
||||
}
|
||||
for (key in o2) {
|
||||
if (!(key in keySet) &&
|
||||
key.charAt(0) !== '$' &&
|
||||
isDefined(o2[key]) &&
|
||||
!isFunction(o2[key])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else if (isDate(o1)) {
|
||||
if (!isDate(o2)) return false;
|
||||
return equals(o1.getTime(), o2.getTime());
|
||||
} else if (isRegExp(o1)) {
|
||||
if (!isRegExp(o2)) return false;
|
||||
return o1.toString() == o2.toString();
|
||||
} else {
|
||||
if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
|
||||
isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
|
||||
keySet = createMap();
|
||||
for (key in o1) {
|
||||
if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
|
||||
if (!equals(o1[key], o2[key])) return false;
|
||||
keySet[key] = true;
|
||||
}
|
||||
for (key in o2) {
|
||||
if (!(key in keySet) &&
|
||||
key.charAt(0) !== '$' &&
|
||||
isDefined(o2[key]) &&
|
||||
!isFunction(o2[key])) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -1405,17 +1404,10 @@ function getNgAttribute(element, ngAttr) {
|
||||
* designates the **root element** of the application and is typically placed near the root element
|
||||
* of the page - e.g. on the `<body>` or `<html>` tags.
|
||||
*
|
||||
* There are a few things to keep in mind when using `ngApp`:
|
||||
* - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
|
||||
* found in the document will be used to define the root element to auto-bootstrap as an
|
||||
* application. To run multiple applications in an HTML document you must manually bootstrap them using
|
||||
* {@link angular.bootstrap} instead.
|
||||
* - AngularJS applications cannot be nested within each other.
|
||||
* - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
|
||||
* This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
|
||||
* {@link ngRoute.ngView `ngView`}.
|
||||
* Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
|
||||
* causing animations to stop working and making the injector inaccessible from outside the app.
|
||||
* Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
|
||||
* found in the document will be used to define the root element to auto-bootstrap as an
|
||||
* application. To run multiple applications in an HTML document you must manually bootstrap them using
|
||||
* {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
|
||||
*
|
||||
* You can specify an **AngularJS module** to be used as the root module for the application. This
|
||||
* module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
|
||||
@@ -1555,25 +1547,16 @@ function angularInit(element, bootstrap) {
|
||||
* @description
|
||||
* Use this function to manually start up angular application.
|
||||
*
|
||||
* For more information, see the {@link guide/bootstrap Bootstrap guide}.
|
||||
* See: {@link guide/bootstrap Bootstrap}
|
||||
*
|
||||
* Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
|
||||
* They must use {@link ng.directive:ngApp ngApp}.
|
||||
*
|
||||
* Angular will detect if it has been loaded into the browser more than once and only allow the
|
||||
* first loaded script to be bootstrapped and will report a warning to the browser console for
|
||||
* each of the subsequent scripts. This prevents strange results in applications, where otherwise
|
||||
* multiple instances of Angular try to work on the DOM.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
|
||||
* They must use {@link ng.directive:ngApp ngApp}.
|
||||
* </div>
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
|
||||
* such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
|
||||
* Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
|
||||
* causing animations to stop working and making the injector inaccessible from outside the app.
|
||||
* </div>
|
||||
*
|
||||
* ```html
|
||||
* <!doctype html>
|
||||
* <html>
|
||||
@@ -1621,7 +1604,7 @@ function bootstrap(element, modules, config) {
|
||||
//Encode angle brackets to prevent input from being sanitized to empty string #8683
|
||||
throw ngMinErr(
|
||||
'btstrpd',
|
||||
"App Already Bootstrapped with this Element '{0}'",
|
||||
"App already bootstrapped with this element '{0}'",
|
||||
tag.replace(/</,'<').replace(/>/,'>'));
|
||||
}
|
||||
|
||||
@@ -1716,6 +1699,7 @@ function snake_case(name, separator) {
|
||||
}
|
||||
|
||||
var bindJQueryFired = false;
|
||||
var skipDestroyOnNextJQueryCleanData;
|
||||
function bindJQuery() {
|
||||
var originalCleanData;
|
||||
|
||||
@@ -1749,11 +1733,15 @@ function bindJQuery() {
|
||||
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');
|
||||
if (!skipDestroyOnNextJQueryCleanData) {
|
||||
for (var i = 0, elem; (elem = elems[i]) != null; i++) {
|
||||
events = jQuery._data(elem, "events");
|
||||
if (events && events.$destroy) {
|
||||
jQuery(elem).triggerHandler('$destroy');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
skipDestroyOnNextJQueryCleanData = false;
|
||||
}
|
||||
originalCleanData(elems);
|
||||
};
|
||||
|
||||
@@ -63,7 +63,6 @@
|
||||
$BrowserProvider,
|
||||
$CacheFactoryProvider,
|
||||
$ControllerProvider,
|
||||
$DateProvider,
|
||||
$DocumentProvider,
|
||||
$ExceptionHandlerProvider,
|
||||
$FilterProvider,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, document, undefined) {
|
||||
|
||||
@@ -62,23 +62,17 @@
|
||||
* Implicit module which gets automatically added to each {@link auto.$injector $injector}.
|
||||
*/
|
||||
|
||||
var ARROW_ARG = /^([^\(]+?)=>/;
|
||||
var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
|
||||
var FN_ARG_SPLIT = /,/;
|
||||
var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
|
||||
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
||||
var $injectorMinErr = minErr('$injector');
|
||||
|
||||
function extractArgs(fn) {
|
||||
var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
|
||||
args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
|
||||
return args;
|
||||
}
|
||||
|
||||
function anonFn(fn) {
|
||||
// For anonymous functions, showing at the very least the function signature can help in
|
||||
// debugging.
|
||||
var args = extractArgs(fn);
|
||||
var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
|
||||
args = fnText.match(FN_ARGS);
|
||||
if (args) {
|
||||
return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
|
||||
}
|
||||
@@ -87,6 +81,7 @@ function anonFn(fn) {
|
||||
|
||||
function annotate(fn, strictDi, name) {
|
||||
var $inject,
|
||||
fnText,
|
||||
argDecl,
|
||||
last;
|
||||
|
||||
@@ -101,7 +96,8 @@ function annotate(fn, strictDi, name) {
|
||||
throw $injectorMinErr('strictdi',
|
||||
'{0} is not using explicit annotation and cannot be invoked in strict mode', name);
|
||||
}
|
||||
argDecl = extractArgs(fn);
|
||||
fnText = fn.toString().replace(STRIP_COMMENTS, '');
|
||||
argDecl = fnText.match(FN_ARGS);
|
||||
forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
|
||||
arg.replace(FN_ARG, function(all, underscore, name) {
|
||||
$inject.push(name);
|
||||
@@ -656,19 +652,14 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
|
||||
})),
|
||||
instanceCache = {},
|
||||
protoInstanceInjector =
|
||||
instanceInjector = (instanceCache.$injector =
|
||||
createInternalInjector(instanceCache, function(serviceName, caller) {
|
||||
var provider = providerInjector.get(serviceName + providerSuffix, caller);
|
||||
return instanceInjector.invoke(
|
||||
provider.$get, provider, undefined, serviceName);
|
||||
}),
|
||||
instanceInjector = protoInstanceInjector;
|
||||
return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
|
||||
}));
|
||||
|
||||
providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
|
||||
var runBlocks = loadModules(modulesToLoad);
|
||||
instanceInjector = protoInstanceInjector.get('$injector');
|
||||
instanceInjector.strictDi = strictDi;
|
||||
forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
|
||||
|
||||
forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
|
||||
|
||||
return instanceInjector;
|
||||
|
||||
@@ -818,66 +809,47 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function injectionArgs(fn, locals, serviceName) {
|
||||
var args = [],
|
||||
$inject = createInjector.$$annotate(fn, strictDi, serviceName);
|
||||
|
||||
for (var i = 0, length = $inject.length; i < length; i++) {
|
||||
var key = $inject[i];
|
||||
if (typeof key !== 'string') {
|
||||
throw $injectorMinErr('itkn',
|
||||
'Incorrect injection token! Expected service name as string, got {0}', key);
|
||||
}
|
||||
args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
|
||||
getService(key, serviceName));
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
function isClass(func) {
|
||||
// IE 9-11 do not support classes and IE9 leaks with the code below.
|
||||
if (msie <= 11) {
|
||||
return false;
|
||||
}
|
||||
// Workaround for MS Edge.
|
||||
// Check https://connect.microsoft.com/IE/Feedback/Details/2211653
|
||||
return typeof func === 'function'
|
||||
&& /^(?:class\s|constructor\()/.test(Function.prototype.toString.call(func));
|
||||
}
|
||||
|
||||
function invoke(fn, self, locals, serviceName) {
|
||||
if (typeof locals === 'string') {
|
||||
serviceName = locals;
|
||||
locals = null;
|
||||
}
|
||||
|
||||
var args = injectionArgs(fn, locals, serviceName);
|
||||
var args = [],
|
||||
$inject = createInjector.$$annotate(fn, strictDi, serviceName),
|
||||
length, i,
|
||||
key;
|
||||
|
||||
for (i = 0, length = $inject.length; i < length; i++) {
|
||||
key = $inject[i];
|
||||
if (typeof key !== 'string') {
|
||||
throw $injectorMinErr('itkn',
|
||||
'Incorrect injection token! Expected service name as string, got {0}', key);
|
||||
}
|
||||
args.push(
|
||||
locals && locals.hasOwnProperty(key)
|
||||
? locals[key]
|
||||
: getService(key, serviceName)
|
||||
);
|
||||
}
|
||||
if (isArray(fn)) {
|
||||
fn = fn[fn.length - 1];
|
||||
fn = fn[length];
|
||||
}
|
||||
|
||||
if (!isClass(fn)) {
|
||||
// http://jsperf.com/angularjs-invoke-apply-vs-switch
|
||||
// #5388
|
||||
return fn.apply(self, args);
|
||||
} else {
|
||||
args.unshift(null);
|
||||
return new (Function.prototype.bind.apply(fn, args))();
|
||||
}
|
||||
// http://jsperf.com/angularjs-invoke-apply-vs-switch
|
||||
// #5388
|
||||
return fn.apply(self, args);
|
||||
}
|
||||
|
||||
|
||||
function instantiate(Type, locals, serviceName) {
|
||||
// Check if Type is annotated and use just the given function at n-1 as parameter
|
||||
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
|
||||
var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
|
||||
var args = injectionArgs(Type, locals, serviceName);
|
||||
// Empty object at position 0 is ignored for invocation with `new`, but required.
|
||||
args.unshift(null);
|
||||
return new (Function.prototype.bind.apply(ctor, args))();
|
||||
}
|
||||
// Object creation: http://jsperf.com/create-constructor/2
|
||||
var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
|
||||
var returnedValue = invoke(Type, instance, locals, serviceName);
|
||||
|
||||
return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
|
||||
}
|
||||
|
||||
return {
|
||||
invoke: invoke,
|
||||
|
||||
@@ -196,12 +196,6 @@ function jqLiteHasData(node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function jqLiteCleanData(nodes) {
|
||||
for (var i = 0, ii = nodes.length; i < ii; i++) {
|
||||
jqLiteRemoveData(nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function jqLiteBuildFragment(html, context) {
|
||||
var tmp, tag, wrap,
|
||||
fragment = context.createDocumentFragment(),
|
||||
@@ -514,7 +508,7 @@ function jqLiteRemove(element, keepData) {
|
||||
function jqLiteDocumentLoaded(action, win) {
|
||||
win = win || window;
|
||||
if (win.document.readyState === 'complete') {
|
||||
// Force the action to be run async for consistent behavior
|
||||
// Force the action to be run async for consistent behaviour
|
||||
// from the action's point of view
|
||||
// i.e. it will definitely not be in a $apply
|
||||
win.setTimeout(action);
|
||||
@@ -600,8 +594,7 @@ function getAliasedAttrName(name) {
|
||||
forEach({
|
||||
data: jqLiteData,
|
||||
removeData: jqLiteRemoveData,
|
||||
hasData: jqLiteHasData,
|
||||
cleanData: jqLiteCleanData
|
||||
hasData: jqLiteHasData
|
||||
}, function(fn, name) {
|
||||
JQLite[name] = fn;
|
||||
});
|
||||
|
||||
@@ -197,9 +197,9 @@ function setupModuleLoader(window) {
|
||||
* @ngdoc method
|
||||
* @name angular.Module#decorator
|
||||
* @module ng
|
||||
* @param {string} The name of the service to decorate.
|
||||
* @param {Function} This function will be invoked when the service needs to be
|
||||
* instantiated and should return the decorated service instance.
|
||||
* @param {string} name The name of the service to decorate.
|
||||
* @param {Function} decorFn This function will be invoked when the service needs to be
|
||||
* instantiated and should return the decorated service instance.
|
||||
* @description
|
||||
* See {@link auto.$provide#decorator $provide.decorator()}.
|
||||
*/
|
||||
@@ -282,19 +282,6 @@ function setupModuleLoader(window) {
|
||||
*/
|
||||
directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#component
|
||||
* @module ng
|
||||
* @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
|
||||
* @param {Object} options Component definition object (a simplified
|
||||
* {@link ng.$compile#directive-definition-object directive definition object})
|
||||
*
|
||||
* @description
|
||||
* See {@link ng.$compileProvider#component $compileProvider.component()}.
|
||||
*/
|
||||
component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name angular.Module#config
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
'use strict';
|
||||
(function() {
|
||||
function isFunction(value) {return typeof value === 'function';};
|
||||
function isFunction(value) {return typeof value === 'function';};
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @license AngularJS v"NG_VERSION_FULL"
|
||||
* (c) 2010-2016 Google, Inc. http://angularjs.org
|
||||
* (c) 2010-2015 Google, Inc. http://angularjs.org
|
||||
* License: MIT
|
||||
*/
|
||||
(function(window, angular, undefined) {
|
||||
|
||||
@@ -41,7 +41,7 @@ function $AnchorScrollProvider() {
|
||||
* When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
|
||||
* current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
|
||||
* in the
|
||||
* [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
|
||||
* [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#an-indicated-part-of-the-document).
|
||||
*
|
||||
* It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
|
||||
* match any anchor whenever it changes. This can be disabled by calling
|
||||
|
||||
@@ -54,7 +54,7 @@ function prepareAnimateOptions(options) {
|
||||
}
|
||||
|
||||
var $$CoreAnimateJsProvider = function() {
|
||||
this.$get = noop;
|
||||
this.$get = function() {};
|
||||
};
|
||||
|
||||
// this is prefixed with Core since it conflicts with
|
||||
@@ -329,8 +329,8 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* // remove all the animation event listeners listening for `enter` on the given element and its children
|
||||
* $animate.off('enter', container);
|
||||
*
|
||||
* // remove the event listener function provided by `callback` that is set
|
||||
* // to listen for `enter` on the given `container` as well as its children
|
||||
* // remove the event listener function provided by `listenerFn` that is set
|
||||
* // to listen for `enter` on the given `element` as well as its children
|
||||
* $animate.off('enter', container, callback);
|
||||
* ```
|
||||
*
|
||||
@@ -553,7 +553,7 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
*
|
||||
* @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
|
||||
* If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
|
||||
* on the provided styles. For example, if a transition animation is set for the given classNamem, then the provided `from` and
|
||||
* on the provided styles. For example, if a transition animation is set for the given className, then the provided `from` and
|
||||
* `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
|
||||
* style in `to`, the style in `from` is applied immediately, and no animation is run.
|
||||
* If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
|
||||
@@ -575,7 +575,7 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
|
||||
* @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
|
||||
* this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
|
||||
* (Note that if no animation is detected then this value will not be applied to the element.)
|
||||
* (Note that if no animation is detected then this value will not be appplied to the element.)
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
|
||||
@@ -36,7 +36,7 @@ var $CoreAnimateCssProvider = function() {
|
||||
options.from = null;
|
||||
}
|
||||
|
||||
/* jshint newcap: false */
|
||||
/* jshint newcap: false*/
|
||||
var closed, runner = new $$AnimateRunner();
|
||||
return {
|
||||
start: run,
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
function Browser(window, document, $log, $sniffer) {
|
||||
var self = this,
|
||||
rawDocument = document[0],
|
||||
location = window.location,
|
||||
history = window.history,
|
||||
setTimeout = window.setTimeout,
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
* When this property is set to true, the HTML compiler will collect DOM nodes between
|
||||
* nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
|
||||
* together as the directive elements. It is recommended that this feature be used on directives
|
||||
* which are not strictly behavioral (such as {@link ngClick}), and which
|
||||
* which are not strictly behavioural (such as {@link ngClick}), and which
|
||||
* do not manipulate or replace child nodes (such as {@link ngInclude}).
|
||||
*
|
||||
* #### `priority`
|
||||
@@ -189,30 +189,6 @@
|
||||
* equality check is done by value (using the {@link angular.equals} function). It's also possible
|
||||
* to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
|
||||
* `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
|
||||
*
|
||||
* * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
|
||||
* expression passed via the attribute `attr`. The expression is evaluated in the context of the
|
||||
* parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the
|
||||
* local name. You can also make the binding optional by adding `?`: `<?` or `<?attr`.
|
||||
*
|
||||
* For example, given `<my-component my-attr="parentModel">` and directive definition of
|
||||
* `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the
|
||||
* value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
|
||||
* in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however
|
||||
* two caveats:
|
||||
* 1. one-way binding does not copy the value from the parent to the isolate scope, it simply
|
||||
* sets the same value. That means if your bound value is an object, changes to its properties
|
||||
* in the isolated scope will be reflected in the parent scope (because both reference the same object).
|
||||
* 2. one-way binding watches changes to the **identity** of the parent value. That means the
|
||||
* {@link ng.$rootScope.Scope#$watch `$watch`} on the parent value only fires if the reference
|
||||
* to the value has changed. In most cases, this should not be of concern, but can be important
|
||||
* to know if you one-way bind to an object, and then replace that object in the isolated scope.
|
||||
* If you now change a property of the object in your parent scope, the change will not be
|
||||
* propagated to the isolated scope, because the identity of the object on the parent scope
|
||||
* has not changed. Instead you must assign a new object.
|
||||
*
|
||||
* One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
|
||||
* back to the parent. However, it does not make this completely impossible.
|
||||
*
|
||||
* * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
|
||||
* no `attr` name is specified then the attribute name is assumed to be the same as the local name.
|
||||
@@ -245,18 +221,8 @@
|
||||
* definition: `controller: 'myCtrl as myAlias'`.
|
||||
*
|
||||
* When an isolate scope is used for a directive (see above), `bindToController: true` will
|
||||
* allow a component to have its properties bound to the controller, rather than to scope.
|
||||
*
|
||||
* After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
|
||||
* properties. You can access these bindings once they have been initialized by providing a controller method called
|
||||
* `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
|
||||
* initialized.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Deprecation warning:** although bindings for non-ES6 class controllers are currently
|
||||
* bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
|
||||
* code that relies upon bindings inside a `$onInit` method on the controller, instead.
|
||||
* </div>
|
||||
* allow a component to have its properties bound to the controller, rather than to scope. When the controller
|
||||
* is instantiated, the initial values of the isolate scope bindings are already available.
|
||||
*
|
||||
* It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
|
||||
* This will set up the scope bindings to the controller directly. Note that `scope` can still be used
|
||||
@@ -276,10 +242,10 @@
|
||||
* * `$element` - Current element
|
||||
* * `$attrs` - Current attributes object for the element
|
||||
* * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
|
||||
* `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
|
||||
* * `scope`: (optional) override the scope.
|
||||
* * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
|
||||
* * `futureParentElement` (optional):
|
||||
* `function([scope], cloneLinkingFn, futureParentElement)`.
|
||||
* * `scope`: optional argument to override the scope.
|
||||
* * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
|
||||
* * `futureParentElement`:
|
||||
* * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
|
||||
* * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
|
||||
* * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
|
||||
@@ -287,34 +253,14 @@
|
||||
* as those elements need to created and cloned in a special way when they are defined outside their
|
||||
* usual containers (e.g. like `<svg>`).
|
||||
* * See also the `directive.templateNamespace` property.
|
||||
* * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
|
||||
* then the default translusion is provided.
|
||||
* The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
|
||||
* `true` if the specified slot contains content (i.e. one or more DOM nodes).
|
||||
*
|
||||
* The controller can provide the following methods that act as life-cycle hooks:
|
||||
* * `$onInit` - Called on each controller after all the controllers on an element have been constructed and
|
||||
* had their bindings initialized (and before the pre & post linking functions for the directives on
|
||||
* this element). This is a good place to put initialization code for your controller.
|
||||
*
|
||||
* #### `require`
|
||||
* Require another directive and inject its controller as the fourth argument to the linking function. The
|
||||
* `require` property can be a string, an array or an object:
|
||||
* * a **string** containing the name of the directive to pass to the linking function
|
||||
* * an **array** containing the names of directives to pass to the linking function. The argument passed to the
|
||||
* linking function will be an array of controllers in the same order as the names in the `require` property
|
||||
* * an **object** whose property values are the names of the directives to pass to the linking function. The argument
|
||||
* passed to the linking function will also be an object with matching keys, whose values will hold the corresponding
|
||||
* controllers.
|
||||
*
|
||||
* If the `require` property is an object and `bindToController` is truthy, then the required controllers are
|
||||
* bound to the controller using the keys of the `require` property. This binding occurs after all the controllers
|
||||
* have been constructed but before `$onInit` is called.
|
||||
* See the {@link $compileProvider#component} helper for an example of how this can be used.
|
||||
*
|
||||
* If no such required directive(s) can be found, or if the directive does not have a controller, then an error is
|
||||
* raised (unless no link function is specified and the required controllers are not being bound to the directive
|
||||
* controller, in which case error checking is skipped). The name can be prefixed with:
|
||||
* `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
|
||||
* injected argument will be an array in corresponding order. If no such directive can be
|
||||
* found, or if the directive does not have a controller, then an error is raised (unless no link function
|
||||
* is specified, in which case error checking is skipped). The name can be prefixed with:
|
||||
*
|
||||
* * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
|
||||
* * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
|
||||
@@ -407,6 +353,14 @@
|
||||
* The contents are compiled and provided to the directive as a **transclusion function**. See the
|
||||
* {@link $compile#transclusion Transclusion} section below.
|
||||
*
|
||||
* There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
|
||||
* directive's element or the entire element:
|
||||
*
|
||||
* * `true` - transclude the content (i.e. the child nodes) of the directive's element.
|
||||
* * `'element'` - transclude the whole of the directive's element including any directives on this
|
||||
* element that defined at a lower priority than this directive. When used, the `template`
|
||||
* property is ignored.
|
||||
*
|
||||
*
|
||||
* #### `compile`
|
||||
*
|
||||
@@ -536,34 +490,6 @@
|
||||
* Testing Transclusion Directives}.
|
||||
* </div>
|
||||
*
|
||||
* There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
|
||||
* directive's element, the entire element or multiple parts of the element contents:
|
||||
*
|
||||
* * `true` - transclude the content (i.e. the child nodes) of the directive's element.
|
||||
* * `'element'` - transclude the whole of the directive's element including any directives on this
|
||||
* element that defined at a lower priority than this directive. When used, the `template`
|
||||
* property is ignored.
|
||||
* * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
|
||||
*
|
||||
* **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
|
||||
*
|
||||
* This object is a map where the keys are the name of the slot to fill and the value is an element selector
|
||||
* used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
|
||||
* and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
|
||||
*
|
||||
* For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
|
||||
*
|
||||
* If the element selector is prefixed with a `?` then that slot is optional.
|
||||
*
|
||||
* For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
|
||||
* the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
|
||||
*
|
||||
* Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
|
||||
* in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
|
||||
* `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
|
||||
* injectable into the directive's controller.
|
||||
*
|
||||
*
|
||||
* #### Transclusion Functions
|
||||
*
|
||||
* When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
|
||||
@@ -584,7 +510,7 @@
|
||||
* content and the `scope` is the newly created transclusion scope, to which the clone is bound.
|
||||
*
|
||||
* <div class="alert alert-info">
|
||||
* **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function
|
||||
* **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
|
||||
* since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
|
||||
* </div>
|
||||
*
|
||||
@@ -616,7 +542,7 @@
|
||||
* </div>
|
||||
*
|
||||
* The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
|
||||
* automatically destroy their transcluded clones as necessary so you do not need to worry about this if
|
||||
* automatically destroy their transluded clones as necessary so you do not need to worry about this if
|
||||
* you are simply using {@link ngTransclude} to inject the transclusion into your directive.
|
||||
*
|
||||
*
|
||||
@@ -854,9 +780,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
var bindingCache = createMap();
|
||||
|
||||
function parseIsolateBindings(scope, directiveName, isController) {
|
||||
var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
|
||||
var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
|
||||
|
||||
var bindings = {};
|
||||
var bindings = createMap();
|
||||
|
||||
forEach(scope, function(definition, scopeName) {
|
||||
if (definition in bindingCache) {
|
||||
@@ -948,11 +874,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
* @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
|
||||
* will match as <code>ng-bind</code>), or an object map of directives where the keys are the
|
||||
* names and the values are the factories.
|
||||
* @param {Function|Array} directiveFactory An injectable directive factory function. See the
|
||||
* {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
|
||||
* @param {Function|Array} directiveFactory An injectable directive factory function. See
|
||||
* {@link guide/directive} for more info.
|
||||
* @returns {ng.$compileProvider} Self for chaining.
|
||||
*/
|
||||
this.directive = function registerDirective(name, directiveFactory) {
|
||||
this.directive = function registerDirective(name, directiveFactory) {
|
||||
assertNotHasOwnProperty(name, 'directive');
|
||||
if (isString(name)) {
|
||||
assertValidDirectiveName(name);
|
||||
@@ -991,131 +917,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $compileProvider#component
|
||||
* @module ng
|
||||
* @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
|
||||
* @param {Object} options Component definition object (a simplified
|
||||
* {@link ng.$compile#directive-definition-object directive definition object}),
|
||||
* with the following properties (all optional):
|
||||
*
|
||||
* - `controller` – `{(string|function()=}` – controller constructor function that should be
|
||||
* associated with newly created scope or the name of a {@link ng.$compile#-controller-
|
||||
* registered controller} if passed as a string. An empty `noop` function by default.
|
||||
* - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope.
|
||||
* If present, the controller will be published to scope under the `controllerAs` name.
|
||||
* If not present, this will default to be `$ctrl`.
|
||||
* - `template` – `{string=|function()=}` – html template as a string or a function that
|
||||
* returns an html template as a string which should be used as the contents of this component.
|
||||
* Empty string by default.
|
||||
*
|
||||
* If `template` is a function, then it is {@link auto.$injector#invoke injected} with
|
||||
* the following locals:
|
||||
*
|
||||
* - `$element` - Current element
|
||||
* - `$attrs` - Current attributes object for the element
|
||||
*
|
||||
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
|
||||
* template that should be used as the contents of this component.
|
||||
*
|
||||
* If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
|
||||
* the following locals:
|
||||
*
|
||||
* - `$element` - Current element
|
||||
* - `$attrs` - Current attributes object for the element
|
||||
*
|
||||
* - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties.
|
||||
* Component properties are always bound to the component controller and not to the scope.
|
||||
* See {@link ng.$compile#-bindtocontroller- `bindToController`}.
|
||||
* - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
|
||||
* Disabled by default.
|
||||
* - `$...` – additional properties to attach to the directive factory function and the controller
|
||||
* constructor function. (This is used by the component router to annotate)
|
||||
*
|
||||
* @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
|
||||
* @description
|
||||
* Register a **component definition** with the compiler. This is a shorthand for registering a special
|
||||
* type of directive, which represents a self-contained UI component in your application. Such components
|
||||
* are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`).
|
||||
*
|
||||
* Component definitions are very simple and do not require as much configuration as defining general
|
||||
* directives. Component definitions usually consist only of a template and a controller backing it.
|
||||
*
|
||||
* In order to make the definition easier, components enforce best practices like use of `controllerAs`,
|
||||
* `bindToController`. They always have **isolate scope** and are restricted to elements.
|
||||
*
|
||||
* Here are a few examples of how you would usually define components:
|
||||
*
|
||||
* ```js
|
||||
* var myMod = angular.module(...);
|
||||
* myMod.component('myComp', {
|
||||
* template: '<div>My name is {{$ctrl.name}}</div>',
|
||||
* controller: function() {
|
||||
* this.name = 'shahar';
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* myMod.component('myComp', {
|
||||
* template: '<div>My name is {{$ctrl.name}}</div>',
|
||||
* bindings: {name: '@'}
|
||||
* });
|
||||
*
|
||||
* myMod.component('myComp', {
|
||||
* templateUrl: 'views/my-comp.html',
|
||||
* controller: 'MyCtrl',
|
||||
* controllerAs: 'ctrl',
|
||||
* bindings: {name: '@'}
|
||||
* });
|
||||
*
|
||||
* ```
|
||||
* For more examples, and an in-depth guide, see the {@link guide/component component guide}.
|
||||
*
|
||||
* <br />
|
||||
* See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
|
||||
*/
|
||||
this.component = function registerComponent(name, options) {
|
||||
var controller = options.controller || noop;
|
||||
|
||||
function factory($injector) {
|
||||
function makeInjectable(fn) {
|
||||
if (isFunction(fn) || isArray(fn)) {
|
||||
return function(tElement, tAttrs) {
|
||||
return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
|
||||
};
|
||||
} else {
|
||||
return fn;
|
||||
}
|
||||
}
|
||||
|
||||
var template = (!options.template && !options.templateUrl ? '' : options.template);
|
||||
return {
|
||||
controller: controller,
|
||||
controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
|
||||
template: makeInjectable(template),
|
||||
templateUrl: makeInjectable(options.templateUrl),
|
||||
transclude: options.transclude,
|
||||
scope: {},
|
||||
bindToController: options.bindings || {},
|
||||
restrict: 'E',
|
||||
require: options.require
|
||||
};
|
||||
}
|
||||
|
||||
// Copy any annotation properties (starting with $) over to the factory function
|
||||
// These could be used by libraries such as the new component router
|
||||
forEach(options, function(val, key) {
|
||||
if (key.charAt(0) === '$') {
|
||||
factory[key] = val;
|
||||
controller[key] = val;
|
||||
}
|
||||
});
|
||||
|
||||
factory.$inject = ['$injector'];
|
||||
|
||||
return this.directive(name, factory);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
@@ -1213,9 +1014,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
|
||||
$controller, $rootScope, $sce, $animate, $$sanitizeUri) {
|
||||
|
||||
var SIMPLE_ATTR_NAME = /^\w/;
|
||||
var specialAttrHolder = document.createElement('div');
|
||||
function Attributes(element, attributesToCopy) {
|
||||
var Attributes = function(element, attributesToCopy) {
|
||||
if (attributesToCopy) {
|
||||
var keys = Object.keys(attributesToCopy);
|
||||
var i, l, key;
|
||||
@@ -1229,7 +1028,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
|
||||
this.$$element = element;
|
||||
}
|
||||
};
|
||||
|
||||
Attributes.prototype = {
|
||||
/**
|
||||
@@ -1350,11 +1149,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
nodeName = nodeName_(this.$$element);
|
||||
|
||||
if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) ||
|
||||
if ((nodeName === 'a' && key === 'href') ||
|
||||
(nodeName === 'img' && key === 'src')) {
|
||||
// sanitize a[href] and img[src] values
|
||||
this[key] = value = $$sanitizeUri(value, key === 'src');
|
||||
} else if (nodeName === 'img' && key === 'srcset') {
|
||||
} else if (nodeName === 'img' && key === 'srcset' && isDefined(value)) {
|
||||
// sanitize img[srcset] values
|
||||
var result = "";
|
||||
|
||||
@@ -1394,11 +1193,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (value === null || isUndefined(value)) {
|
||||
this.$$element.removeAttr(attrName);
|
||||
} else {
|
||||
if (SIMPLE_ATTR_NAME.test(attrName)) {
|
||||
this.$$element.attr(attrName, value);
|
||||
} else {
|
||||
setSpecialAttr(this.$$element[0], attrName, value);
|
||||
}
|
||||
this.$$element.attr(attrName, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1452,18 +1247,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
};
|
||||
|
||||
function setSpecialAttr(element, attrName, value) {
|
||||
// Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
|
||||
// so we have to jump through some hoops to get such an attribute
|
||||
// https://github.com/angular/angular.js/pull/13318
|
||||
specialAttrHolder.innerHTML = "<span " + attrName + ">";
|
||||
var attributes = specialAttrHolder.firstChild.attributes;
|
||||
var attribute = attributes[0];
|
||||
// We have to remove the attribute from its container element before we can add it to the destination element
|
||||
attributes.removeNamedItem(attribute.name);
|
||||
attribute.value = value;
|
||||
element.attributes.setNamedItem(attribute);
|
||||
}
|
||||
|
||||
function safeAddClass($element, className) {
|
||||
try {
|
||||
@@ -1510,14 +1293,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
|
||||
} : noop;
|
||||
|
||||
compile.$$createComment = function(directiveName, comment) {
|
||||
var content = '';
|
||||
if (debugInfoEnabled) {
|
||||
content = ' ' + (directiveName || '') + ': ' + (comment || '') + ' ';
|
||||
}
|
||||
return document.createComment(content);
|
||||
};
|
||||
|
||||
return compile;
|
||||
|
||||
//================================
|
||||
@@ -1612,7 +1387,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (!node) {
|
||||
return 'html';
|
||||
} else {
|
||||
return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html';
|
||||
return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1731,7 +1506,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
|
||||
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
|
||||
function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
|
||||
|
||||
var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
|
||||
|
||||
if (!transcludedScope) {
|
||||
transcludedScope = scope.$new(false, containingScope);
|
||||
@@ -1743,18 +1519,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
transcludeControllers: controllers,
|
||||
futureParentElement: futureParentElement
|
||||
});
|
||||
}
|
||||
|
||||
// We need to attach the transclusion slots onto the `boundTranscludeFn`
|
||||
// so that they are available inside the `controllersBoundTransclude` function
|
||||
var boundSlots = boundTranscludeFn.$$slots = createMap();
|
||||
for (var slotName in transcludeFn.$$slots) {
|
||||
if (transcludeFn.$$slots[slotName]) {
|
||||
boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
|
||||
} else {
|
||||
boundSlots[slotName] = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return boundTranscludeFn;
|
||||
}
|
||||
@@ -1908,41 +1673,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
* @returns {Function}
|
||||
*/
|
||||
function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
|
||||
return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
|
||||
return function(scope, element, attrs, controllers, transcludeFn) {
|
||||
element = groupScan(element[0], attrStart, attrEnd);
|
||||
return linkFn(scope, element, attrs, controllers, transcludeFn);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A function generator that is used to support both eager and lazy compilation
|
||||
* linking function.
|
||||
* @param eager
|
||||
* @param $compileNodes
|
||||
* @param transcludeFn
|
||||
* @param maxPriority
|
||||
* @param ignoreDirective
|
||||
* @param previousCompileContext
|
||||
* @returns {Function}
|
||||
*/
|
||||
function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
|
||||
var compiled;
|
||||
|
||||
if (eager) {
|
||||
return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
|
||||
}
|
||||
return function lazyCompilation() {
|
||||
if (!compiled) {
|
||||
compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
|
||||
|
||||
// Null out all of these references in order to make them eligible for garbage collection
|
||||
// since this is a potentially long lived closure
|
||||
$compileNodes = transcludeFn = previousCompileContext = null;
|
||||
}
|
||||
return compiled.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Once the directives have been collected, their compile functions are executed. This method
|
||||
* is responsible for inlining directive templates as well as terminating the application
|
||||
@@ -1987,8 +1723,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
replaceDirective = originalReplaceDirective,
|
||||
childTranscludeFn = transcludeFn,
|
||||
linkFn,
|
||||
didScanForMultipleTransclusion = false,
|
||||
mightHaveMultipleTransclusionError = false,
|
||||
directiveValue;
|
||||
|
||||
// executes all directives on the current element
|
||||
@@ -2031,27 +1765,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
directiveName = directive.name;
|
||||
|
||||
// If we encounter a condition that can result in transclusion on the directive,
|
||||
// then scan ahead in the remaining directives for others that may cause a multiple
|
||||
// transclusion error to be thrown during the compilation process. If a matching directive
|
||||
// is found, then we know that when we encounter a transcluded directive, we need to eagerly
|
||||
// compile the `transclude` function rather than doing it lazily in order to throw
|
||||
// exceptions at the correct time
|
||||
if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
|
||||
|| (directive.transclude && !directive.$$tlb))) {
|
||||
var candidateDirective;
|
||||
|
||||
for (var scanningIndex = i + 1; candidateDirective = directives[scanningIndex++];) {
|
||||
if ((candidateDirective.transclude && !candidateDirective.$$tlb)
|
||||
|| (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
|
||||
mightHaveMultipleTransclusionError = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
didScanForMultipleTransclusion = true;
|
||||
}
|
||||
|
||||
if (!directive.templateUrl && directive.controller) {
|
||||
directiveValue = directive.controller;
|
||||
controllerDirectives = controllerDirectives || createMap();
|
||||
@@ -2076,11 +1789,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
terminalPriority = directive.priority;
|
||||
$template = $compileNode;
|
||||
$compileNode = templateAttrs.$$element =
|
||||
jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName]));
|
||||
jqLite(document.createComment(' ' + directiveName + ': ' +
|
||||
templateAttrs[directiveName] + ' '));
|
||||
compileNode = $compileNode[0];
|
||||
replaceWith(jqCollection, sliceArgs($template), compileNode);
|
||||
|
||||
childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
|
||||
childTranscludeFn = compile($template, transcludeFn, terminalPriority,
|
||||
replaceDirective && replaceDirective.name, {
|
||||
// Don't pass in:
|
||||
// - controllerDirectives - otherwise we'll create duplicates controllers
|
||||
@@ -2092,69 +1806,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
nonTlbTranscludeDirective: nonTlbTranscludeDirective
|
||||
});
|
||||
} else {
|
||||
|
||||
var slots = createMap();
|
||||
|
||||
$template = jqLite(jqLiteClone(compileNode)).contents();
|
||||
|
||||
if (isObject(directiveValue)) {
|
||||
|
||||
// We have transclusion slots,
|
||||
// collect them up, compile them and store their transclusion functions
|
||||
$template = [];
|
||||
|
||||
var slotMap = createMap();
|
||||
var filledSlots = createMap();
|
||||
|
||||
// Parse the element selectors
|
||||
forEach(directiveValue, function(elementSelector, slotName) {
|
||||
// If an element selector starts with a ? then it is optional
|
||||
var optional = (elementSelector.charAt(0) === '?');
|
||||
elementSelector = optional ? elementSelector.substring(1) : elementSelector;
|
||||
|
||||
slotMap[elementSelector] = slotName;
|
||||
|
||||
// We explicitly assign `null` since this implies that a slot was defined but not filled.
|
||||
// Later when calling boundTransclusion functions with a slot name we only error if the
|
||||
// slot is `undefined`
|
||||
slots[slotName] = null;
|
||||
|
||||
// filledSlots contains `true` for all slots that are either optional or have been
|
||||
// filled. This is used to check that we have not missed any required slots
|
||||
filledSlots[slotName] = optional;
|
||||
});
|
||||
|
||||
// Add the matching elements into their slot
|
||||
forEach($compileNode.contents(), function(node) {
|
||||
var slotName = slotMap[directiveNormalize(nodeName_(node))];
|
||||
if (slotName) {
|
||||
filledSlots[slotName] = true;
|
||||
slots[slotName] = slots[slotName] || [];
|
||||
slots[slotName].push(node);
|
||||
} else {
|
||||
$template.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
// Check for required slots that were not filled
|
||||
forEach(filledSlots, function(filled, slotName) {
|
||||
if (!filled) {
|
||||
throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
|
||||
}
|
||||
});
|
||||
|
||||
for (var slotName in slots) {
|
||||
if (slots[slotName]) {
|
||||
// Only define a transclusion function if the slot was filled
|
||||
slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$compileNode.empty(); // clear contents
|
||||
childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
|
||||
childTranscludeFn = compile($template, transcludeFn, undefined,
|
||||
undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
|
||||
childTranscludeFn.$$slots = slots;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2282,8 +1937,76 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getControllers(directiveName, require, $element, elementControllers) {
|
||||
var value;
|
||||
|
||||
if (isString(require)) {
|
||||
var match = require.match(REQUIRE_PREFIX_REGEXP);
|
||||
var name = require.substring(match[0].length);
|
||||
var inheritType = match[1] || match[3];
|
||||
var optional = match[2] === '?';
|
||||
|
||||
//If only parents then start at the parent element
|
||||
if (inheritType === '^^') {
|
||||
$element = $element.parent();
|
||||
//Otherwise attempt getting the controller from elementControllers in case
|
||||
//the element is transcluded (and has no data) and to avoid .data if possible
|
||||
} else {
|
||||
value = elementControllers && elementControllers[name];
|
||||
value = value && value.instance;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
var dataName = '$' + name + 'Controller';
|
||||
value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
|
||||
}
|
||||
|
||||
if (!value && !optional) {
|
||||
throw $compileMinErr('ctreq',
|
||||
"Controller '{0}', required by directive '{1}', can't be found!",
|
||||
name, directiveName);
|
||||
}
|
||||
} else if (isArray(require)) {
|
||||
value = [];
|
||||
for (var i = 0, ii = require.length; i < ii; i++) {
|
||||
value[i] = getControllers(directiveName, require[i], $element, elementControllers);
|
||||
}
|
||||
}
|
||||
|
||||
return value || null;
|
||||
}
|
||||
|
||||
function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
|
||||
var elementControllers = createMap();
|
||||
for (var controllerKey in controllerDirectives) {
|
||||
var directive = controllerDirectives[controllerKey];
|
||||
var locals = {
|
||||
$scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
|
||||
$element: $element,
|
||||
$attrs: attrs,
|
||||
$transclude: transcludeFn
|
||||
};
|
||||
|
||||
var controller = directive.controller;
|
||||
if (controller == '@') {
|
||||
controller = attrs[directive.name];
|
||||
}
|
||||
|
||||
var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
|
||||
|
||||
// For directives with element transclusion the element is a comment.
|
||||
// In this case .data will not attach any data.
|
||||
// Instead, we save the controllers for the element in a local hash and attach to .data
|
||||
// later, once we have the actual element.
|
||||
elementControllers[directive.name] = controllerInstance;
|
||||
$element.data('$' + directive.name + 'Controller', controllerInstance.instance);
|
||||
}
|
||||
return elementControllers;
|
||||
}
|
||||
|
||||
function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
|
||||
var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
|
||||
var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
|
||||
attrs, removeScopeBindingWatches, removeControllerBindingWatches;
|
||||
|
||||
if (compileNode === linkNode) {
|
||||
@@ -2306,14 +2029,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
// is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
|
||||
transcludeFn = controllersBoundTransclude;
|
||||
transcludeFn.$$boundTransclude = boundTranscludeFn;
|
||||
// expose the slots on the `$transclude` function
|
||||
transcludeFn.isSlotFilled = function(slotName) {
|
||||
return !!boundTranscludeFn.$$slots[slotName];
|
||||
};
|
||||
}
|
||||
|
||||
if (controllerDirectives) {
|
||||
elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
|
||||
elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
|
||||
}
|
||||
|
||||
if (newIsolateScopeDirective) {
|
||||
@@ -2354,21 +2073,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
}
|
||||
|
||||
// Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
|
||||
forEach(controllerDirectives, function(controllerDirective, name) {
|
||||
var require = controllerDirective.require;
|
||||
if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
|
||||
extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
|
||||
}
|
||||
});
|
||||
|
||||
// Trigger the `$onInit` method on all controllers that have one
|
||||
forEach(elementControllers, function(controller) {
|
||||
if (isFunction(controller.instance.$onInit)) {
|
||||
controller.instance.$onInit();
|
||||
}
|
||||
});
|
||||
|
||||
// PRELINKING
|
||||
for (i = 0, ii = preLinkFns.length; i < ii; i++) {
|
||||
linkFn = preLinkFns[i];
|
||||
@@ -2404,11 +2108,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
// This is the function that is injected as `$transclude`.
|
||||
// Note: all arguments are optional!
|
||||
function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
|
||||
function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
|
||||
var transcludeControllers;
|
||||
|
||||
// No scope passed in:
|
||||
if (!isScope(scope)) {
|
||||
slotName = futureParentElement;
|
||||
futureParentElement = cloneAttachFn;
|
||||
cloneAttachFn = scope;
|
||||
scope = undefined;
|
||||
@@ -2420,99 +2124,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (!futureParentElement) {
|
||||
futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
|
||||
}
|
||||
if (slotName) {
|
||||
// slotTranscludeFn can be one of three things:
|
||||
// * a transclude function - a filled slot
|
||||
// * `null` - an optional slot that was not filled
|
||||
// * `undefined` - a slot that was not declared (i.e. invalid)
|
||||
var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
|
||||
if (slotTranscludeFn) {
|
||||
return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
|
||||
} else if (isUndefined(slotTranscludeFn)) {
|
||||
throw $compileMinErr('noslot',
|
||||
'No parent directive that requires a transclusion with slot name "{0}". ' +
|
||||
'Element: {1}',
|
||||
slotName, startingTag($element));
|
||||
}
|
||||
} else {
|
||||
return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
|
||||
}
|
||||
return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getControllers(directiveName, require, $element, elementControllers) {
|
||||
var value;
|
||||
|
||||
if (isString(require)) {
|
||||
var match = require.match(REQUIRE_PREFIX_REGEXP);
|
||||
var name = require.substring(match[0].length);
|
||||
var inheritType = match[1] || match[3];
|
||||
var optional = match[2] === '?';
|
||||
|
||||
//If only parents then start at the parent element
|
||||
if (inheritType === '^^') {
|
||||
$element = $element.parent();
|
||||
//Otherwise attempt getting the controller from elementControllers in case
|
||||
//the element is transcluded (and has no data) and to avoid .data if possible
|
||||
} else {
|
||||
value = elementControllers && elementControllers[name];
|
||||
value = value && value.instance;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
var dataName = '$' + name + 'Controller';
|
||||
value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
|
||||
}
|
||||
|
||||
if (!value && !optional) {
|
||||
throw $compileMinErr('ctreq',
|
||||
"Controller '{0}', required by directive '{1}', can't be found!",
|
||||
name, directiveName);
|
||||
}
|
||||
} else if (isArray(require)) {
|
||||
value = [];
|
||||
for (var i = 0, ii = require.length; i < ii; i++) {
|
||||
value[i] = getControllers(directiveName, require[i], $element, elementControllers);
|
||||
}
|
||||
} else if (isObject(require)) {
|
||||
value = {};
|
||||
forEach(require, function(controller, property) {
|
||||
value[property] = getControllers(directiveName, controller, $element, elementControllers);
|
||||
});
|
||||
}
|
||||
|
||||
return value || null;
|
||||
}
|
||||
|
||||
function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
|
||||
var elementControllers = createMap();
|
||||
for (var controllerKey in controllerDirectives) {
|
||||
var directive = controllerDirectives[controllerKey];
|
||||
var locals = {
|
||||
$scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
|
||||
$element: $element,
|
||||
$attrs: attrs,
|
||||
$transclude: transcludeFn
|
||||
};
|
||||
|
||||
var controller = directive.controller;
|
||||
if (controller == '@') {
|
||||
controller = attrs[directive.name];
|
||||
}
|
||||
|
||||
var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
|
||||
|
||||
// For directives with element transclusion the element is a comment.
|
||||
// In this case .data will not attach any data.
|
||||
// Instead, we save the controllers for the element in a local hash and attach to .data
|
||||
// later, once we have the actual element.
|
||||
elementControllers[directive.name] = controllerInstance;
|
||||
$element.data('$' + directive.name + 'Controller', controllerInstance.instance);
|
||||
}
|
||||
return elementControllers;
|
||||
}
|
||||
|
||||
// Depending upon the context in which a directive finds itself it might need to have a new isolated
|
||||
// or child scope created. For instance:
|
||||
// * if the directive has been pulled into a template because another directive with a higher priority
|
||||
@@ -2947,14 +2563,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
parent.replaceChild(newNode, firstElementToRemove);
|
||||
}
|
||||
|
||||
// Append all the `elementsToRemove` to a fragment. This will...
|
||||
// - remove them from the DOM
|
||||
// - allow them to still be traversed with .nextSibling
|
||||
// - allow a single fragment.qSA to fetch all elements being removed
|
||||
// TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
|
||||
var fragment = document.createDocumentFragment();
|
||||
for (i = 0; i < removeCount; i++) {
|
||||
fragment.appendChild(elementsToRemove[i]);
|
||||
}
|
||||
fragment.appendChild(firstElementToRemove);
|
||||
|
||||
if (jqLite.hasData(firstElementToRemove)) {
|
||||
// Copy over user data (that includes Angular's $scope etc.). Don't copy private
|
||||
@@ -2962,18 +2573,31 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
// event listeners (which is the main use of private data) wouldn't work anyway.
|
||||
jqLite.data(newNode, jqLite.data(firstElementToRemove));
|
||||
|
||||
// Remove $destroy event listeners from `firstElementToRemove`
|
||||
jqLite(firstElementToRemove).off('$destroy');
|
||||
// Remove data of the replaced element. We cannot just call .remove()
|
||||
// on the element it since that would deallocate scope that is needed
|
||||
// for the new node. Instead, remove the data "manually".
|
||||
if (!jQuery) {
|
||||
delete jqLite.cache[firstElementToRemove[jqLite.expando]];
|
||||
} else {
|
||||
// jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
|
||||
// the replaced element. The cleanData version monkey-patched by Angular would cause
|
||||
// the scope to be trashed and we do need the very same scope to work with the new
|
||||
// element. However, we cannot just cache the non-patched version and use it here as
|
||||
// that would break if another library patches the method after Angular does (one
|
||||
// example is jQuery UI). Instead, set a flag indicating scope destroying should be
|
||||
// skipped this one time.
|
||||
skipDestroyOnNextJQueryCleanData = true;
|
||||
jQuery.cleanData([firstElementToRemove]);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup any data/listeners on the elements and children.
|
||||
// This includes invoking the $destroy event on any elements with listeners.
|
||||
jqLite.cleanData(fragment.querySelectorAll('*'));
|
||||
|
||||
// Update the jqLite collection to only contain the `newNode`
|
||||
for (i = 1; i < removeCount; i++) {
|
||||
delete elementsToRemove[i];
|
||||
for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
|
||||
var element = elementsToRemove[k];
|
||||
jqLite(element).remove(); // must do this way to clean up expando
|
||||
fragment.appendChild(element);
|
||||
delete elementsToRemove[k];
|
||||
}
|
||||
|
||||
elementsToRemove[0] = newNode;
|
||||
elementsToRemove.length = 1;
|
||||
}
|
||||
@@ -2997,12 +2621,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
// only occurs for isolate scopes and new scopes with controllerAs.
|
||||
function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
|
||||
var removeWatchCollection = [];
|
||||
forEach(bindings, function initializeBinding(definition, scopeName) {
|
||||
forEach(bindings, function(definition, scopeName) {
|
||||
var attrName = definition.attrName,
|
||||
optional = definition.optional,
|
||||
mode = definition.mode, // @, =, or &
|
||||
lastValue,
|
||||
parentGet, parentSet, compare, removeWatch;
|
||||
parentGet, parentSet, compare;
|
||||
|
||||
switch (mode) {
|
||||
|
||||
@@ -3039,7 +2663,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (parentGet.literal) {
|
||||
compare = equals;
|
||||
} else {
|
||||
compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
|
||||
compare = function(a, b) { return a === b || (a !== a && b !== b); };
|
||||
}
|
||||
parentSet = parentGet.assign || function() {
|
||||
// reset the change, or we will throw this exception on every $digest
|
||||
@@ -3063,6 +2687,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
return lastValue = parentValue;
|
||||
};
|
||||
parentValueWatch.$stateful = true;
|
||||
var removeWatch;
|
||||
if (definition.collection) {
|
||||
removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
|
||||
} else {
|
||||
@@ -3071,24 +2696,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
removeWatchCollection.push(removeWatch);
|
||||
break;
|
||||
|
||||
case '<':
|
||||
if (!hasOwnProperty.call(attrs, attrName)) {
|
||||
if (optional) break;
|
||||
attrs[attrName] = void 0;
|
||||
}
|
||||
if (optional && !attrs[attrName]) break;
|
||||
|
||||
parentGet = $parse(attrs[attrName]);
|
||||
|
||||
destination[scopeName] = parentGet(scope);
|
||||
|
||||
removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newParentValue) {
|
||||
destination[scopeName] = newParentValue;
|
||||
}, parentGet.literal);
|
||||
|
||||
removeWatchCollection.push(removeWatch);
|
||||
break;
|
||||
|
||||
case '&':
|
||||
// Don't assign Object.prototype method to scope
|
||||
parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
|
||||
|
||||
@@ -27,15 +27,6 @@ function $ControllerProvider() {
|
||||
var controllers = {},
|
||||
globals = false;
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $controllerProvider#has
|
||||
* @param {string} name Controller name to check.
|
||||
*/
|
||||
this.has = function(name) {
|
||||
return controllers.hasOwnProperty(name);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $controllerProvider#register
|
||||
@@ -92,7 +83,7 @@ function $ControllerProvider() {
|
||||
* It's just a simple call to {@link auto.$injector $injector}, but extracted into
|
||||
* a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
|
||||
*/
|
||||
return function $controller(expression, locals, later, ident) {
|
||||
return function(expression, locals, later, ident) {
|
||||
// PRIVATE API:
|
||||
// param `later` --- indicates that the controller's constructor is invoked at a later time.
|
||||
// If true, $controller will allocate the object with the correct
|
||||
@@ -143,7 +134,7 @@ function $ControllerProvider() {
|
||||
}
|
||||
|
||||
var instantiate;
|
||||
return instantiate = extend(function $controllerInit() {
|
||||
return instantiate = extend(function() {
|
||||
var result = $injector.invoke(expression, instance, locals, constructor);
|
||||
if (result !== instance && (isObject(result) || isFunction(result))) {
|
||||
instance = result;
|
||||
|
||||
@@ -127,7 +127,7 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
|
||||
*
|
||||
* However, if the method is used programmatically, for example by adding dynamically created controls,
|
||||
* or controls that have been previously removed without destroying their corresponding DOM element,
|
||||
* it's the developers responsibility to make sure the current state propagates to the parent form.
|
||||
* it's the developers responsiblity to make sure the current state propagates to the parent form.
|
||||
*
|
||||
* For example, if an input control is added that is already `$dirty` and has `$error` properties,
|
||||
* calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
ngModelMinErr: false,
|
||||
*/
|
||||
|
||||
// Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
|
||||
var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/;
|
||||
// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
|
||||
var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
|
||||
// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
|
||||
// Note: We are being more lenient, because browsers are too.
|
||||
// 1. Scheme
|
||||
@@ -26,10 +26,10 @@ var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-
|
||||
var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
|
||||
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
|
||||
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
|
||||
var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
|
||||
var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
|
||||
var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
|
||||
var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
|
||||
var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
|
||||
var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
|
||||
var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
|
||||
var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
|
||||
var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
|
||||
|
||||
var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
|
||||
@@ -62,8 +62,8 @@ var inputType = {
|
||||
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
|
||||
* that contains the regular expression body that will be converted to a regular expression
|
||||
* as in the ngPattern directive.
|
||||
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
|
||||
* does not match a RegExp found by evaluating the Angular expression given in the attribute value.
|
||||
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
|
||||
* a RegExp found by evaluating the Angular expression given in the attribute value.
|
||||
* If the expression evaluates to a RegExp object, then this is used directly.
|
||||
* If the expression evaluates to a string, then it will be converted to a RegExp
|
||||
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
|
||||
@@ -350,7 +350,7 @@ var inputType = {
|
||||
*
|
||||
* @description
|
||||
* Input with time validation and transformation. In browsers that do not yet support
|
||||
* the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
|
||||
* the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
|
||||
* local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
|
||||
* Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
|
||||
*
|
||||
@@ -393,7 +393,7 @@ var inputType = {
|
||||
}]);
|
||||
</script>
|
||||
<form name="myForm" ng-controller="DateController as dateCtrl">
|
||||
<label for="exampleInput">Pick a between 8am and 5pm:</label>
|
||||
<label for="exampleInput">Pick a time between 8am and 5pm:</label>
|
||||
<input type="time" id="exampleInput" name="input" ng-model="example.value"
|
||||
placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
|
||||
<div role="alert">
|
||||
@@ -697,8 +697,8 @@ var inputType = {
|
||||
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
|
||||
* that contains the regular expression body that will be converted to a regular expression
|
||||
* as in the ngPattern directive.
|
||||
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
|
||||
* does not match a RegExp found by evaluating the Angular expression given in the attribute value.
|
||||
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
|
||||
* a RegExp found by evaluating the Angular expression given in the attribute value.
|
||||
* If the expression evaluates to a RegExp object, then this is used directly.
|
||||
* If the expression evaluates to a string, then it will be converted to a RegExp
|
||||
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
|
||||
@@ -795,8 +795,8 @@ var inputType = {
|
||||
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
|
||||
* that contains the regular expression body that will be converted to a regular expression
|
||||
* as in the ngPattern directive.
|
||||
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
|
||||
* does not match a RegExp found by evaluating the Angular expression given in the attribute value.
|
||||
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
|
||||
* a RegExp found by evaluating the Angular expression given in the attribute value.
|
||||
* If the expression evaluates to a RegExp object, then this is used directly.
|
||||
* If the expression evaluates to a string, then it will be converted to a RegExp
|
||||
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
|
||||
@@ -894,8 +894,8 @@ var inputType = {
|
||||
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
|
||||
* that contains the regular expression body that will be converted to a regular expression
|
||||
* as in the ngPattern directive.
|
||||
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
|
||||
* does not match a RegExp found by evaluating the Angular expression given in the attribute value.
|
||||
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
|
||||
* a RegExp found by evaluating the Angular expression given in the attribute value.
|
||||
* If the expression evaluates to a RegExp object, then this is used directly.
|
||||
* If the expression evaluates to a string, then it will be converted to a RegExp
|
||||
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
|
||||
@@ -1114,7 +1114,7 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
if (!$sniffer.android) {
|
||||
var composing = false;
|
||||
|
||||
element.on('compositionstart', function() {
|
||||
element.on('compositionstart', function(data) {
|
||||
composing = true;
|
||||
});
|
||||
|
||||
@@ -1375,7 +1375,11 @@ function badInputChecker(scope, element, attr, ctrl) {
|
||||
if (nativeValidation) {
|
||||
ctrl.$parsers.push(function(value) {
|
||||
var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
|
||||
return validity.badInput || validity.typeMismatch ? undefined : value;
|
||||
// Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
|
||||
// - also sets validity.badInput (should only be validity.typeMismatch).
|
||||
// - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
|
||||
// - can ignore this case as we can still read out the erroneous email...
|
||||
return validity.badInput && !validity.typeMismatch ? undefined : value;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1547,8 +1551,8 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
|
||||
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
|
||||
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
|
||||
* length.
|
||||
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
|
||||
* does not match a RegExp found by evaluating the Angular expression given in the attribute value.
|
||||
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
|
||||
* a RegExp found by evaluating the Angular expression given in the attribute value.
|
||||
* If the expression evaluates to a RegExp object, then this is used directly.
|
||||
* If the expression evaluates to a string, then it will be converted to a RegExp
|
||||
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
|
||||
@@ -1586,8 +1590,8 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
|
||||
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
|
||||
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
|
||||
* length.
|
||||
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
|
||||
* value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
|
||||
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
|
||||
* a RegExp found by evaluating the Angular expression given in the attribute value.
|
||||
* If the expression evaluates to a RegExp object, then this is used directly.
|
||||
* If the expression evaluates to a string, then it will be converted to a RegExp
|
||||
* after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
|
||||
|
||||
@@ -188,8 +188,9 @@ var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse,
|
||||
restrict: 'A',
|
||||
compile: function ngBindHtmlCompile(tElement, tAttrs) {
|
||||
var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
|
||||
var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
|
||||
return (value || '').toString();
|
||||
var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function sceValueOf(val) {
|
||||
// Unwrap the value to compare the actual inner safe value, not the wrapper object.
|
||||
return $sce.valueOf(val);
|
||||
});
|
||||
$compile.$$addBindingClass(tElement);
|
||||
|
||||
@@ -197,9 +198,9 @@ var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse,
|
||||
$compile.$$addBindingInfo(element, attr.ngBindHtml);
|
||||
|
||||
scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
|
||||
// we re-evaluate the expr because we want a TrustedValueHolderType
|
||||
// for $sce, not a string
|
||||
element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
|
||||
// The watched value is the unwrapped value. To avoid re-escaping, use the direct getter.
|
||||
var value = ngBindHtmlGetter(scope);
|
||||
element.html($sce.getTrustedHtml(value) || '');
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -78,7 +78,11 @@ function classDirective(name, selector) {
|
||||
updateClasses(oldClasses, newClasses);
|
||||
}
|
||||
}
|
||||
oldVal = shallowCopy(newVal);
|
||||
if (isArray(newVal)) {
|
||||
oldVal = newVal.map(function(v) { return shallowCopy(v); });
|
||||
} else {
|
||||
oldVal = shallowCopy(newVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -148,10 +152,9 @@ function classDirective(name, selector) {
|
||||
* new classes added.
|
||||
*
|
||||
* @animations
|
||||
* | Animation | Occurs |
|
||||
* |----------------------------------|-------------------------------------|
|
||||
* | {@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 |
|
||||
* **add** - happens just before the class is applied to the elements
|
||||
*
|
||||
* **remove** - happens just before the class is removed from the element
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngClass {@link guide/expression Expression} to eval. The result
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
*
|
||||
* * no-inline-style: this stops Angular from injecting CSS styles into the DOM
|
||||
*
|
||||
* * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings
|
||||
* * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
|
||||
*
|
||||
* You can use these values in the following combinations:
|
||||
*
|
||||
@@ -64,7 +64,7 @@
|
||||
* inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
|
||||
*
|
||||
* * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
|
||||
* run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
|
||||
* run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
|
||||
*
|
||||
* * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
|
||||
* styles nor use eval, which is the same as an empty: ng-csp.
|
||||
|
||||
@@ -34,10 +34,8 @@
|
||||
* and `leave` effects.
|
||||
*
|
||||
* @animations
|
||||
* | Animation | Occurs |
|
||||
* |----------------------------------|-------------------------------------|
|
||||
* | {@link ng.$animate#enter enter} | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container |
|
||||
* | {@link ng.$animate#leave leave} | just before the `ngIf` contents are removed from the DOM |
|
||||
* enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
|
||||
* leave - happens just before the `ngIf` contents are removed from the DOM
|
||||
*
|
||||
* @element ANY
|
||||
* @scope
|
||||
@@ -78,7 +76,7 @@
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
|
||||
var ngIfDirective = ['$animate', function($animate) {
|
||||
return {
|
||||
multiElement: true,
|
||||
transclude: 'element',
|
||||
@@ -94,7 +92,7 @@ var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
|
||||
if (!childScope) {
|
||||
$transclude(function(clone, newScope) {
|
||||
childScope = newScope;
|
||||
clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf);
|
||||
clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
|
||||
// Note: We only need the first/last node of the cloned nodes.
|
||||
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
|
||||
// by a directive with templateUrl when its template arrives.
|
||||
|
||||
@@ -23,10 +23,8 @@
|
||||
* access on some browsers.
|
||||
*
|
||||
* @animations
|
||||
* | Animation | Occurs |
|
||||
* |----------------------------------|-------------------------------------|
|
||||
* | {@link ng.$animate#enter enter} | when the expression changes, on the new include |
|
||||
* | {@link ng.$animate#leave leave} | when the expression changes, on the old include |
|
||||
* enter - animation is used to bring new content into the browser.
|
||||
* leave - animation is used to animate existing content away.
|
||||
*
|
||||
* The enter and leave animation occur concurrently.
|
||||
*
|
||||
@@ -287,7 +285,7 @@ var ngIncludeFillContentDirective = ['$compile',
|
||||
priority: -400,
|
||||
require: 'ngInclude',
|
||||
link: function(scope, $element, $attr, ctrl) {
|
||||
if (toString.call($element[0]).match(/SVG/)) {
|
||||
if (/SVG/.test($element[0].toString())) {
|
||||
// WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
|
||||
// support innerHTML, so detect this here and try to generate the contents
|
||||
// specially.
|
||||
|
||||
@@ -14,9 +14,7 @@ var VALID_CLASS = 'ng-valid',
|
||||
DIRTY_CLASS = 'ng-dirty',
|
||||
UNTOUCHED_CLASS = 'ng-untouched',
|
||||
TOUCHED_CLASS = 'ng-touched',
|
||||
PENDING_CLASS = 'ng-pending',
|
||||
EMPTY_CLASS = 'ng-empty',
|
||||
NOT_EMPTY_CLASS = 'ng-not-empty';
|
||||
PENDING_CLASS = 'ng-pending';
|
||||
|
||||
var ngModelMinErr = minErr('ngModel');
|
||||
|
||||
@@ -265,9 +263,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
};
|
||||
ngModelSet = function($scope, newValue) {
|
||||
if (isFunction(parsedNgModel($scope))) {
|
||||
invokeModelSetter($scope, {$$$p: newValue});
|
||||
invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
|
||||
} else {
|
||||
parsedNgModelAssign($scope, newValue);
|
||||
parsedNgModelAssign($scope, ctrl.$modelValue);
|
||||
}
|
||||
};
|
||||
} else if (!parsedNgModel.assign) {
|
||||
@@ -292,7 +290,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
* the `$viewValue` are different from last time.
|
||||
*
|
||||
* Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
|
||||
* `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue`
|
||||
* `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
|
||||
* or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
|
||||
* invoked if you only change a property on the objects.
|
||||
*/
|
||||
@@ -320,17 +318,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
return isUndefined(value) || value === '' || value === null || value !== value;
|
||||
};
|
||||
|
||||
this.$$updateEmptyClasses = function(value) {
|
||||
if (ctrl.$isEmpty(value)) {
|
||||
$animate.removeClass($element, NOT_EMPTY_CLASS);
|
||||
$animate.addClass($element, EMPTY_CLASS);
|
||||
} else {
|
||||
$animate.removeClass($element, EMPTY_CLASS);
|
||||
$animate.addClass($element, NOT_EMPTY_CLASS);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var currentValidationRunId = 0;
|
||||
|
||||
/**
|
||||
@@ -644,7 +631,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
setValidity(name, undefined);
|
||||
validatorPromises.push(promise.then(function() {
|
||||
setValidity(name, true);
|
||||
}, function() {
|
||||
}, function(error) {
|
||||
allValid = false;
|
||||
setValidity(name, false);
|
||||
}));
|
||||
@@ -694,7 +681,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
|
||||
return;
|
||||
}
|
||||
ctrl.$$updateEmptyClasses(viewValue);
|
||||
ctrl.$$lastCommittedViewValue = viewValue;
|
||||
|
||||
// change to dirty
|
||||
@@ -793,7 +779,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
* However, custom controls might also pass objects to this method. In this case, we should make
|
||||
* a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
|
||||
* perform a deep watch of objects, it only looks for a change of identity. If you only change
|
||||
* the property of the object then ngModel will not realize that the object has changed and
|
||||
* the property of the object then ngModel will not realise that the object has changed and
|
||||
* will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
|
||||
* not change properties of the copy once it has been passed to `$setViewValue`.
|
||||
* Otherwise you may cause the model value on the scope to change incorrectly.
|
||||
@@ -877,7 +863,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
viewValue = formatters[idx](viewValue);
|
||||
}
|
||||
if (ctrl.$viewValue !== viewValue) {
|
||||
ctrl.$$updateEmptyClasses(viewValue);
|
||||
ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
|
||||
ctrl.$render();
|
||||
|
||||
@@ -908,8 +893,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
* require.
|
||||
* - Providing validation behavior (i.e. required, number, email, url).
|
||||
* - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
|
||||
* - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
|
||||
* `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
|
||||
* - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
|
||||
* - Registering the control with its parent {@link ng.directive:form form}.
|
||||
*
|
||||
* Note: `ngModel` will try to bind to the property given by evaluating the expression on the
|
||||
@@ -966,16 +950,13 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
* - `ng-touched`: the control has been blurred
|
||||
* - `ng-untouched`: the control hasn't been blurred
|
||||
* - `ng-pending`: any `$asyncValidators` are unfulfilled
|
||||
* - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
|
||||
* by the {@link ngModel.NgModelController#$isEmpty} method
|
||||
* - `ng-not-empty`: the view contains a non-empty value
|
||||
*
|
||||
* Keep in mind that ngAnimate can detect each of these classes when added and removed.
|
||||
*
|
||||
* ## Animation Hooks
|
||||
*
|
||||
* Animations within models are triggered when any of the associated CSS classes are added and removed
|
||||
* on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
|
||||
* on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
|
||||
* `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
|
||||
* The animations that are triggered within ngModel are similar to how they work in ngClass and
|
||||
* animations can be hooked into using CSS transitions, keyframes as well as JS animations.
|
||||
@@ -1118,7 +1099,7 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
|
||||
});
|
||||
}
|
||||
|
||||
element.on('blur', function() {
|
||||
element.on('blur', function(ev) {
|
||||
if (modelCtrl.$touched) return;
|
||||
|
||||
if ($rootScope.$$phase) {
|
||||
|
||||
@@ -342,8 +342,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
|
||||
var value = optionValues[key];
|
||||
|
||||
var locals = getLocals(value, key);
|
||||
var selectValue = getTrackByValueFn(value, locals);
|
||||
var locals = getLocals(optionValues[key], key);
|
||||
var selectValue = getTrackByValueFn(optionValues[key], locals);
|
||||
watchedArray.push(selectValue);
|
||||
|
||||
// Only need to watch the displayFn if there is a specific label expression
|
||||
@@ -409,10 +409,14 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
var optionTemplate = document.createElement('option'),
|
||||
optGroupTemplate = document.createElement('optgroup');
|
||||
|
||||
|
||||
function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
|
||||
|
||||
var selectCtrl = ctrls[0];
|
||||
// if ngModel is not defined, we don't need to do anything
|
||||
var ngModelCtrl = ctrls[1];
|
||||
if (!ngModelCtrl) return;
|
||||
|
||||
var selectCtrl = ctrls[0];
|
||||
var multiple = attr.multiple;
|
||||
|
||||
// The emptyOption allows the application developer to provide their own custom "empty"
|
||||
@@ -678,7 +682,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
var groupElement;
|
||||
var optionElement;
|
||||
|
||||
if (isDefined(option.group)) {
|
||||
if (option.group) {
|
||||
|
||||
// This option is to live in a group
|
||||
// See if we have already created this group
|
||||
@@ -752,7 +756,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
terminal: true,
|
||||
require: ['select', 'ngModel'],
|
||||
require: ['select', '?ngModel'],
|
||||
link: {
|
||||
pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
|
||||
// Deactivate the SelectController.register method to prevent
|
||||
|
||||
@@ -215,7 +215,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
|
||||
}
|
||||
|
||||
// If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
|
||||
// In JS `NaN !== NaN`, so we have to explicitly check.
|
||||
// In JS `NaN !== NaN`, so we have to exlicitly check.
|
||||
if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
|
||||
watchRemover();
|
||||
var whenExpFn = whensExpFns[count];
|
||||
|
||||
@@ -36,23 +36,17 @@
|
||||
* <div ng-repeat="(key, value) in myObj"> ... </div>
|
||||
* ```
|
||||
*
|
||||
* However, there are a limitations compared to array iteration:
|
||||
* You need to be aware that the JavaScript specification does not define the order of keys
|
||||
* returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
|
||||
* used to sort the keys alphabetically.)
|
||||
*
|
||||
* - The JavaScript specification does not define the order of keys
|
||||
* returned for an object, so Angular relies on the order returned by the browser
|
||||
* when running `for key in myObj`. Browsers generally follow the strategy of providing
|
||||
* keys in the order in which they were defined, although there are exceptions when keys are deleted
|
||||
* and reinstated. See the
|
||||
* [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
|
||||
* Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
|
||||
* when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
|
||||
* keys in the order in which they were defined, although there are exceptions when keys are deleted
|
||||
* and reinstated. See the [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
|
||||
*
|
||||
* - `ngRepeat` will silently *ignore* object keys starting with `$`, because
|
||||
* it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
|
||||
*
|
||||
* - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
|
||||
* objects, and will throw if used with one.
|
||||
*
|
||||
* If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
|
||||
* that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
|
||||
* If this is not desired, the recommended workaround is to convert your object into an array
|
||||
* that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
|
||||
* do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
|
||||
* or implement a `$watch` on the object yourself.
|
||||
*
|
||||
@@ -99,7 +93,7 @@
|
||||
* by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
|
||||
* will not have to rebuild the DOM elements for items it has already rendered, even if the
|
||||
* JavaScript objects in the collection have been substituted for new ones. For large collections,
|
||||
* this significantly improves rendering performance. If you don't have a unique identifier,
|
||||
* this signifincantly improves rendering performance. If you don't have a unique identifier,
|
||||
* `track by $index` can also provide a performance boost.
|
||||
* </div>
|
||||
* ```html
|
||||
@@ -170,13 +164,11 @@
|
||||
* as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
|
||||
*
|
||||
* @animations
|
||||
* | Animation | Occurs |
|
||||
* |----------------------------------|-------------------------------------|
|
||||
* | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
|
||||
* | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
|
||||
* | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
|
||||
* **.enter** - when a new item is added to the list or when an item is revealed after a filter
|
||||
*
|
||||
* See the example below for defining CSS animations with ngRepeat.
|
||||
* **.leave** - when an item is removed from the list or when an item is filtered out
|
||||
*
|
||||
* **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
|
||||
*
|
||||
* @element ANY
|
||||
* @scope
|
||||
@@ -230,11 +222,22 @@
|
||||
* For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
|
||||
*
|
||||
* @example
|
||||
* This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
|
||||
* results by name. New (entering) and removed (leaving) items are animated.
|
||||
<example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true">
|
||||
* This example initializes the scope to a list of names and
|
||||
* then uses `ngRepeat` to display every person:
|
||||
<example module="ngAnimate" deps="angular-animate.js" animations="true">
|
||||
<file name="index.html">
|
||||
<div ng-controller="repeatController">
|
||||
<div ng-init="friends = [
|
||||
{name:'John', age:25, gender:'boy'},
|
||||
{name:'Jessie', age:30, gender:'girl'},
|
||||
{name:'Johanna', age:28, gender:'girl'},
|
||||
{name:'Joy', age:15, gender:'girl'},
|
||||
{name:'Mary', age:28, gender:'girl'},
|
||||
{name:'Peter', age:95, gender:'boy'},
|
||||
{name:'Sebastian', age:50, gender:'boy'},
|
||||
{name:'Erika', age:27, gender:'girl'},
|
||||
{name:'Patrick', age:40, gender:'boy'},
|
||||
{name:'Samantha', age:60, gender:'girl'}
|
||||
]">
|
||||
I have {{friends.length}} friends. They are:
|
||||
<input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
|
||||
<ul class="example-animate-container">
|
||||
@@ -247,22 +250,6 @@
|
||||
</ul>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
|
||||
$scope.friends = [
|
||||
{name:'John', age:25, gender:'boy'},
|
||||
{name:'Jessie', age:30, gender:'girl'},
|
||||
{name:'Johanna', age:28, gender:'girl'},
|
||||
{name:'Joy', age:15, gender:'girl'},
|
||||
{name:'Mary', age:28, gender:'girl'},
|
||||
{name:'Peter', age:95, gender:'boy'},
|
||||
{name:'Sebastian', age:50, gender:'boy'},
|
||||
{name:'Erika', age:27, gender:'girl'},
|
||||
{name:'Patrick', age:40, gender:'boy'},
|
||||
{name:'Samantha', age:60, gender:'girl'}
|
||||
];
|
||||
});
|
||||
</file>
|
||||
<file name="animations.css">
|
||||
.example-animate-container {
|
||||
background:white;
|
||||
@@ -273,7 +260,7 @@
|
||||
}
|
||||
|
||||
.animate-repeat {
|
||||
line-height:30px;
|
||||
line-height:40px;
|
||||
list-style:none;
|
||||
box-sizing:border-box;
|
||||
}
|
||||
@@ -295,7 +282,7 @@
|
||||
.animate-repeat.ng-move.ng-move-active,
|
||||
.animate-repeat.ng-enter.ng-enter-active {
|
||||
opacity:1;
|
||||
max-height:30px;
|
||||
max-height:40px;
|
||||
}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
@@ -322,7 +309,7 @@
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
|
||||
var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||
var NG_REMOVED = '$$NG_REMOVED';
|
||||
var ngRepeatMinErr = minErr('ngRepeat');
|
||||
|
||||
@@ -357,7 +344,7 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
|
||||
$$tlb: true,
|
||||
compile: function ngRepeatCompile($element, $attr) {
|
||||
var expression = $attr.ngRepeat;
|
||||
var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
|
||||
var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
|
||||
|
||||
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
|
||||
|
||||
|
||||
@@ -85,14 +85,12 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
|
||||
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
|
||||
* ```
|
||||
*
|
||||
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
|
||||
* Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
|
||||
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
|
||||
*
|
||||
* @animations
|
||||
* | Animation | Occurs |
|
||||
* |----------------------------------|-------------------------------------|
|
||||
* | {@link $animate#addClass addClass} `.ng-hide` | after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden |
|
||||
* | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible |
|
||||
* addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
|
||||
* removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngShow If the {@link guide/expression expression} is truthy
|
||||
@@ -251,15 +249,12 @@ var ngShowDirective = ['$animate', function($animate) {
|
||||
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
|
||||
* ```
|
||||
*
|
||||
* Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
|
||||
* Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
|
||||
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
|
||||
*
|
||||
* @animations
|
||||
* | Animation | Occurs |
|
||||
* |----------------------------------|-------------------------------------|
|
||||
* | {@link $animate#addClass addClass} `.ng-hide` | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden |
|
||||
* | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible |
|
||||
*
|
||||
* removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
|
||||
* addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngHide If the {@link guide/expression expression} is truthy then
|
||||
|
||||
@@ -27,10 +27,8 @@
|
||||
* </div>
|
||||
|
||||
* @animations
|
||||
* | Animation | Occurs |
|
||||
* |----------------------------------|-------------------------------------|
|
||||
* | {@link ng.$animate#enter enter} | after the ngSwitch contents change and the matched child element is placed inside the container |
|
||||
* | {@link ng.$animate#leave leave} | after the ngSwitch contents change and just before the former contents are removed from the DOM |
|
||||
* enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
|
||||
* leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
|
||||
*
|
||||
* @usage
|
||||
*
|
||||
@@ -129,7 +127,7 @@
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
|
||||
var ngSwitchDirective = ['$animate', function($animate) {
|
||||
return {
|
||||
require: 'ngSwitch',
|
||||
|
||||
@@ -170,7 +168,7 @@ var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
|
||||
selectedTransclude.transclude(function(caseElement, selectedScope) {
|
||||
selectedScopes.push(selectedScope);
|
||||
var anchor = selectedTransclude.element;
|
||||
caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
|
||||
caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
|
||||
var block = { clone: caseElement };
|
||||
|
||||
selectedElements.push(block);
|
||||
|
||||
@@ -8,186 +8,66 @@
|
||||
* @description
|
||||
* Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
|
||||
*
|
||||
* You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
|
||||
* as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
|
||||
*
|
||||
* If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
|
||||
* content of this element will be removed before the transcluded content is inserted.
|
||||
* If the transcluded content is empty, the existing content is left intact. This lets you provide fallback content in the case
|
||||
* that no transcluded content is provided.
|
||||
* Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
|
||||
*
|
||||
* @element ANY
|
||||
*
|
||||
* @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
|
||||
* or its value is the same as the name of the attribute then the default slot is used.
|
||||
*
|
||||
* @example
|
||||
* ### Basic transclusion
|
||||
* This example demonstrates basic transclusion of content into a component directive.
|
||||
* <example name="simpleTranscludeExample" module="transcludeExample">
|
||||
* <file name="index.html">
|
||||
* <script>
|
||||
* angular.module('transcludeExample', [])
|
||||
* .directive('pane', function(){
|
||||
* return {
|
||||
* restrict: 'E',
|
||||
* transclude: true,
|
||||
* scope: { title:'@' },
|
||||
* template: '<div style="border: 1px solid black;">' +
|
||||
* '<div style="background-color: gray">{{title}}</div>' +
|
||||
* '<ng-transclude></ng-transclude>' +
|
||||
* '</div>'
|
||||
* };
|
||||
* })
|
||||
* .controller('ExampleController', ['$scope', function($scope) {
|
||||
* $scope.title = 'Lorem Ipsum';
|
||||
* $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
|
||||
* }]);
|
||||
* </script>
|
||||
* <div ng-controller="ExampleController">
|
||||
* <input ng-model="title" aria-label="title"> <br/>
|
||||
* <textarea ng-model="text" aria-label="text"></textarea> <br/>
|
||||
* <pane title="{{title}}">{{text}}</pane>
|
||||
* </div>
|
||||
* </file>
|
||||
* <file name="protractor.js" type="protractor">
|
||||
* it('should have transcluded', function() {
|
||||
* var titleElement = element(by.model('title'));
|
||||
* titleElement.clear();
|
||||
* titleElement.sendKeys('TITLE');
|
||||
* var textElement = element(by.model('text'));
|
||||
* textElement.clear();
|
||||
* textElement.sendKeys('TEXT');
|
||||
* expect(element(by.binding('title')).getText()).toEqual('TITLE');
|
||||
* expect(element(by.binding('text')).getText()).toEqual('TEXT');
|
||||
* });
|
||||
* </file>
|
||||
* </example>
|
||||
<example module="transcludeExample">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
angular.module('transcludeExample', [])
|
||||
.directive('pane', function(){
|
||||
return {
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: { title:'@' },
|
||||
template: '<div style="border: 1px solid black;">' +
|
||||
'<div style="background-color: gray">{{title}}</div>' +
|
||||
'<ng-transclude></ng-transclude>' +
|
||||
'</div>'
|
||||
};
|
||||
})
|
||||
.controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.title = 'Lorem Ipsum';
|
||||
$scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
|
||||
}]);
|
||||
</script>
|
||||
<div ng-controller="ExampleController">
|
||||
<input ng-model="title" aria-label="title"> <br/>
|
||||
<textarea ng-model="text" aria-label="text"></textarea> <br/>
|
||||
<pane title="{{title}}">{{text}}</pane>
|
||||
</div>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should have transcluded', function() {
|
||||
var titleElement = element(by.model('title'));
|
||||
titleElement.clear();
|
||||
titleElement.sendKeys('TITLE');
|
||||
var textElement = element(by.model('text'));
|
||||
textElement.clear();
|
||||
textElement.sendKeys('TEXT');
|
||||
expect(element(by.binding('title')).getText()).toEqual('TITLE');
|
||||
expect(element(by.binding('text')).getText()).toEqual('TEXT');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*
|
||||
* @example
|
||||
* ### Transclude fallback content
|
||||
* This example shows how to use `NgTransclude` with fallback content, that
|
||||
* is displayed if no transcluded content is provided.
|
||||
*
|
||||
* <example module="transcludeFallbackContentExample">
|
||||
* <file name="index.html">
|
||||
* <script>
|
||||
* angular.module('transcludeFallbackContentExample', [])
|
||||
* .directive('myButton', function(){
|
||||
* return {
|
||||
* restrict: 'E',
|
||||
* transclude: true,
|
||||
* scope: true,
|
||||
* template: '<button style="cursor: pointer;">' +
|
||||
* '<ng-transclude>' +
|
||||
* '<b style="color: red;">Button1</b>' +
|
||||
* '</ng-transclude>' +
|
||||
* '</button>'
|
||||
* };
|
||||
* });
|
||||
* </script>
|
||||
* <!-- fallback button content -->
|
||||
* <my-button id="fallback"></my-button>
|
||||
* <!-- modified button content -->
|
||||
* <my-button id="modified">
|
||||
* <i style="color: green;">Button2</i>
|
||||
* </my-button>
|
||||
* </file>
|
||||
* <file name="protractor.js" type="protractor">
|
||||
* it('should have different transclude element content', function() {
|
||||
* expect(element(by.id('fallback')).getText()).toBe('Button1');
|
||||
* expect(element(by.id('modified')).getText()).toBe('Button2');
|
||||
* });
|
||||
* </file>
|
||||
* </example>
|
||||
*
|
||||
* @example
|
||||
* ### Multi-slot transclusion
|
||||
* This example demonstrates using multi-slot transclusion in a component directive.
|
||||
* <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
|
||||
* <file name="index.html">
|
||||
* <style>
|
||||
* .title, .footer {
|
||||
* background-color: gray
|
||||
* }
|
||||
* </style>
|
||||
* <div ng-controller="ExampleController">
|
||||
* <input ng-model="title" aria-label="title"> <br/>
|
||||
* <textarea ng-model="text" aria-label="text"></textarea> <br/>
|
||||
* <pane>
|
||||
* <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
|
||||
* <pane-body><p>{{text}}</p></pane-body>
|
||||
* </pane>
|
||||
* </div>
|
||||
* </file>
|
||||
* <file name="app.js">
|
||||
* angular.module('multiSlotTranscludeExample', [])
|
||||
* .directive('pane', function(){
|
||||
* return {
|
||||
* restrict: 'E',
|
||||
* transclude: {
|
||||
* 'title': '?paneTitle',
|
||||
* 'body': 'paneBody',
|
||||
* 'footer': '?paneFooter'
|
||||
* },
|
||||
* template: '<div style="border: 1px solid black;">' +
|
||||
* '<div class="title" ng-transclude="title">Fallback Title</div>' +
|
||||
* '<div ng-transclude="body"></div>' +
|
||||
* '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
|
||||
* '</div>'
|
||||
* };
|
||||
* })
|
||||
* .controller('ExampleController', ['$scope', function($scope) {
|
||||
* $scope.title = 'Lorem Ipsum';
|
||||
* $scope.link = "https://google.com";
|
||||
* $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
|
||||
* }]);
|
||||
* </file>
|
||||
* <file name="protractor.js" type="protractor">
|
||||
* it('should have transcluded the title and the body', function() {
|
||||
* var titleElement = element(by.model('title'));
|
||||
* titleElement.clear();
|
||||
* titleElement.sendKeys('TITLE');
|
||||
* var textElement = element(by.model('text'));
|
||||
* textElement.clear();
|
||||
* textElement.sendKeys('TEXT');
|
||||
* expect(element(by.css('.title')).getText()).toEqual('TITLE');
|
||||
* expect(element(by.binding('text')).getText()).toEqual('TEXT');
|
||||
* expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer');
|
||||
* });
|
||||
* </file>
|
||||
* </example>
|
||||
*/
|
||||
var ngTranscludeMinErr = minErr('ngTransclude');
|
||||
var ngTranscludeDirective = ngDirective({
|
||||
restrict: 'EAC',
|
||||
link: function($scope, $element, $attrs, controller, $transclude) {
|
||||
|
||||
if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
|
||||
// If the attribute is of the form: `ng-transclude="ng-transclude"`
|
||||
// then treat it like the default
|
||||
$attrs.ngTransclude = '';
|
||||
}
|
||||
|
||||
function ngTranscludeCloneAttachFn(clone) {
|
||||
if (clone.length) {
|
||||
$element.empty();
|
||||
$element.append(clone);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$transclude) {
|
||||
throw ngTranscludeMinErr('orphan',
|
||||
throw minErr('ngTransclude')('orphan',
|
||||
'Illegal use of ngTransclude directive in the template! ' +
|
||||
'No parent directive that requires a transclusion found. ' +
|
||||
'Element: {0}',
|
||||
startingTag($element));
|
||||
}
|
||||
|
||||
// If there is no slot name defined or the slot name is not optional
|
||||
// then transclude the slot
|
||||
var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
|
||||
$transclude(ngTranscludeCloneAttachFn, null, slotName);
|
||||
$transclude(function(clone) {
|
||||
$element.empty();
|
||||
$element.append(clone);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ function chromeHack(optionElement) {
|
||||
* added `<option>` elements, perhaps by an `ngRepeat` directive.
|
||||
*/
|
||||
var SelectController =
|
||||
['$element', '$scope', function($element, $scope) {
|
||||
['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
|
||||
|
||||
var self = this,
|
||||
optionsMap = new HashMap();
|
||||
@@ -167,7 +167,7 @@ var SelectController =
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* Note that the value of a `select` directive used without `ngOptions` is always a string.
|
||||
* When the model needs to be bound to a non-string value, you must either explicitly convert it
|
||||
* When the model needs to be bound to a non-string value, you must either explictly convert it
|
||||
* using a directive (see example below) or use `ngOptions` to specify the set of options.
|
||||
* This is because an option element can only be bound to string values at present.
|
||||
* </div>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngRequired
|
||||
* @restrict A
|
||||
*
|
||||
* @description
|
||||
*
|
||||
|
||||
@@ -93,7 +93,9 @@ function currencyFilter($locale) {
|
||||
* @param {(number|string)=} fractionSize Number of decimal places to round the number to.
|
||||
* If this is not provided then the fraction size is computed from the current locale's number
|
||||
* formatting pattern. In the case of the default locale, it will be 3.
|
||||
* @returns {string} Number rounded to fractionSize and places a “,” after each third digit.
|
||||
* @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current
|
||||
* locale (e.g., in the en_US locale it will have "." as the decimal separator and
|
||||
* include "," group separators after each third digit).
|
||||
*
|
||||
* @example
|
||||
<example module="numberFilterExample">
|
||||
@@ -221,37 +223,18 @@ function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
|
||||
var digit = digits[roundAt];
|
||||
|
||||
if (roundAt > 0) {
|
||||
// Drop fractional digits beyond `roundAt`
|
||||
digits.splice(Math.max(parsedNumber.i, roundAt));
|
||||
|
||||
// Set non-fractional digits beyond `roundAt` to 0
|
||||
for (var j = roundAt; j < digits.length; j++) {
|
||||
digits[j] = 0;
|
||||
}
|
||||
digits.splice(roundAt);
|
||||
} else {
|
||||
// We rounded to zero so reset the parsedNumber
|
||||
fractionLen = Math.max(0, fractionLen);
|
||||
parsedNumber.i = 1;
|
||||
digits.length = Math.max(1, roundAt = fractionSize + 1);
|
||||
digits[0] = 0;
|
||||
for (var i = 1; i < roundAt; i++) digits[i] = 0;
|
||||
digits.length = roundAt = fractionSize + 1;
|
||||
for (var i=0; i < roundAt; i++) digits[i] = 0;
|
||||
}
|
||||
|
||||
if (digit >= 5) {
|
||||
if (roundAt - 1 < 0) {
|
||||
for (var k = 0; k > roundAt; k--) {
|
||||
digits.unshift(0);
|
||||
parsedNumber.i++;
|
||||
}
|
||||
digits.unshift(1);
|
||||
parsedNumber.i++;
|
||||
} else {
|
||||
digits[roundAt - 1]++;
|
||||
}
|
||||
}
|
||||
if (digit >= 5) digits[roundAt - 1]++;
|
||||
|
||||
// Pad out with zeros to get the required fraction length
|
||||
for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
|
||||
for (; fractionLen < fractionSize; fractionLen++) digits.push(0);
|
||||
|
||||
|
||||
// Do any carrying, e.g. a digit was rounded up to 10
|
||||
@@ -315,7 +298,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
|
||||
|
||||
// extract decimals digits
|
||||
if (integerLen > 0) {
|
||||
decimals = digits.splice(integerLen);
|
||||
decimals = digits.splice(integerLen, digits.length);
|
||||
} else {
|
||||
decimals = digits;
|
||||
digits = [0];
|
||||
@@ -323,11 +306,11 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
|
||||
|
||||
// format the integer digits with grouping separators
|
||||
var groups = [];
|
||||
if (digits.length > pattern.lgSize) {
|
||||
groups.unshift(digits.splice(-pattern.lgSize).join(''));
|
||||
if (digits.length >= pattern.lgSize) {
|
||||
groups.unshift(digits.splice(-pattern.lgSize, digits.length).join(''));
|
||||
}
|
||||
while (digits.length > pattern.gSize) {
|
||||
groups.unshift(digits.splice(-pattern.gSize).join(''));
|
||||
groups.unshift(digits.splice(-pattern.gSize, digits.length).join(''));
|
||||
}
|
||||
if (digits.length) {
|
||||
groups.unshift(digits.join(''));
|
||||
@@ -350,15 +333,11 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
|
||||
}
|
||||
}
|
||||
|
||||
function padNumber(num, digits, trim, negWrap) {
|
||||
function padNumber(num, digits, trim) {
|
||||
var neg = '';
|
||||
if (num < 0 || (negWrap && num <= 0)) {
|
||||
if (negWrap) {
|
||||
num = -num + 1;
|
||||
} else {
|
||||
num = -num;
|
||||
neg = '-';
|
||||
}
|
||||
if (num < 0) {
|
||||
neg = '-';
|
||||
num = -num;
|
||||
}
|
||||
num = '' + num;
|
||||
while (num.length < digits) num = ZERO_CHAR + num;
|
||||
@@ -369,7 +348,7 @@ function padNumber(num, digits, trim, negWrap) {
|
||||
}
|
||||
|
||||
|
||||
function dateGetter(name, size, offset, trim, negWrap) {
|
||||
function dateGetter(name, size, offset, trim) {
|
||||
offset = offset || 0;
|
||||
return function(date) {
|
||||
var value = date['get' + name]();
|
||||
@@ -377,15 +356,14 @@ function dateGetter(name, size, offset, trim, negWrap) {
|
||||
value += offset;
|
||||
}
|
||||
if (value === 0 && offset == -12) value = 12;
|
||||
return padNumber(value, size, trim, negWrap);
|
||||
return padNumber(value, size, trim);
|
||||
};
|
||||
}
|
||||
|
||||
function dateStrGetter(name, shortForm, standAlone) {
|
||||
function dateStrGetter(name, shortForm) {
|
||||
return function(date, formats) {
|
||||
var value = date['get' + name]();
|
||||
var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
|
||||
var get = uppercase(propPrefix + name);
|
||||
var get = uppercase(shortForm ? ('SHORT' + name) : name);
|
||||
|
||||
return formats[get][value];
|
||||
};
|
||||
@@ -440,14 +418,13 @@ function longEraGetter(date, formats) {
|
||||
}
|
||||
|
||||
var DATE_FORMATS = {
|
||||
yyyy: dateGetter('FullYear', 4, 0, false, true),
|
||||
yy: dateGetter('FullYear', 2, 0, true, true),
|
||||
y: dateGetter('FullYear', 1, 0, false, true),
|
||||
yyyy: dateGetter('FullYear', 4),
|
||||
yy: dateGetter('FullYear', 2, 0, true),
|
||||
y: dateGetter('FullYear', 1),
|
||||
MMMM: dateStrGetter('Month'),
|
||||
MMM: dateStrGetter('Month', true),
|
||||
MM: dateGetter('Month', 2, 1),
|
||||
M: dateGetter('Month', 1, 1),
|
||||
LLLL: dateStrGetter('Month', false, true),
|
||||
dd: dateGetter('Date', 2),
|
||||
d: dateGetter('Date', 1),
|
||||
HH: dateGetter('Hours', 2),
|
||||
@@ -473,7 +450,7 @@ var DATE_FORMATS = {
|
||||
GGGG: longEraGetter
|
||||
};
|
||||
|
||||
var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
|
||||
var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
|
||||
NUMBER_STRING = /^\-?\d+$/;
|
||||
|
||||
/**
|
||||
@@ -493,7 +470,6 @@ var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+
|
||||
* * `'MMM'`: Month in year (Jan-Dec)
|
||||
* * `'MM'`: Month in year, padded (01-12)
|
||||
* * `'M'`: Month in year (1-12)
|
||||
* * `'LLLL'`: Stand-alone month in year (January-December)
|
||||
* * `'dd'`: Day in month, padded (01-31)
|
||||
* * `'d'`: Day in month (1-31)
|
||||
* * `'EEEE'`: Day in Week,(Sunday-Saturday)
|
||||
|
||||
@@ -9,9 +9,8 @@
|
||||
* Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
|
||||
* for strings and numerically for numbers. Note: if you notice numbers are not being sorted
|
||||
* as expected, make sure they are actually being saved as numbers and not strings.
|
||||
* Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) are also supported.
|
||||
*
|
||||
* @param {Array} array The array (or array-like object) to sort.
|
||||
* @param {Array} array The array to sort.
|
||||
* @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
|
||||
* used by the comparator to determine the order of elements.
|
||||
*
|
||||
@@ -198,10 +197,7 @@ orderByFilter.$inject = ['$parse'];
|
||||
function orderByFilter($parse) {
|
||||
return function(array, sortPredicate, reverseOrder) {
|
||||
|
||||
if (array == null) return array;
|
||||
if (!isArrayLike(array)) {
|
||||
throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
|
||||
}
|
||||
if (!(isArrayLike(array))) return array;
|
||||
|
||||
if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
|
||||
if (sortPredicate.length === 0) { sortPredicate = ['+']; }
|
||||
|
||||
@@ -47,7 +47,7 @@ function $HttpParamSerializerProvider() {
|
||||
forEachSorted(params, function(value, key) {
|
||||
if (value === null || isUndefined(value)) return;
|
||||
if (isArray(value)) {
|
||||
forEach(value, function(v) {
|
||||
forEach(value, function(v, k) {
|
||||
parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
|
||||
});
|
||||
} else {
|
||||
@@ -520,7 +520,7 @@ function $HttpProvider() {
|
||||
*
|
||||
* ```
|
||||
* module.run(function($http) {
|
||||
* $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
|
||||
* $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
@@ -555,7 +555,7 @@ function $HttpProvider() {
|
||||
* That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
|
||||
* For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
|
||||
* function will be reflected on the scope and in any templates where the object is data-bound.
|
||||
* To prevent his, transform functions should have no side-effects.
|
||||
* To prevent this, transform functions should have no side-effects.
|
||||
* If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
|
||||
* </div>
|
||||
*
|
||||
@@ -930,7 +930,7 @@ function $HttpProvider() {
|
||||
*/
|
||||
function $http(requestConfig) {
|
||||
|
||||
if (!isObject(requestConfig)) {
|
||||
if (!angular.isObject(requestConfig)) {
|
||||
throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
|
||||
}
|
||||
|
||||
@@ -1050,7 +1050,7 @@ function $HttpProvider() {
|
||||
|
||||
defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
|
||||
|
||||
// using for-in instead of forEach to avoid unnecessary iteration after header has been found
|
||||
// using for-in instead of forEach to avoid unecessary iteration after header has been found
|
||||
defaultHeadersIteration:
|
||||
for (defHeaderName in defHeaders) {
|
||||
lowercaseDefHeaderName = lowercase(defHeaderName);
|
||||
|
||||
@@ -20,14 +20,6 @@ $interpolateMinErr.interr = function(text, err) {
|
||||
*
|
||||
* Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
|
||||
*
|
||||
* <div class="alert alert-danger">
|
||||
* This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
|
||||
* template within a Python Jinja template (or any other template language). Mixing templating
|
||||
* languages is **very dangerous**. The embedding template language will not safely escape Angular
|
||||
* expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
|
||||
* security bugs!
|
||||
* </div>
|
||||
*
|
||||
* @example
|
||||
<example name="custom-interpolation-markup" module="customInterpolationApp">
|
||||
<file name="index.html">
|
||||
@@ -128,15 +120,6 @@ function $InterpolateProvider() {
|
||||
return value;
|
||||
}
|
||||
|
||||
//TODO: this is the same as the constantWatchDelegate in parse.js
|
||||
function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
|
||||
var unwatch;
|
||||
return unwatch = scope.$watch(function constantInterpolateWatch(scope) {
|
||||
unwatch();
|
||||
return constantInterp(scope);
|
||||
}, listener, objectEquality);
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $interpolate
|
||||
@@ -216,6 +199,11 @@ function $InterpolateProvider() {
|
||||
* </file>
|
||||
* </example>
|
||||
*
|
||||
* @knownIssue
|
||||
* It is currently not possible for an interpolated expression to contain the interpolation end
|
||||
* symbol. For example, `{{ '}}' }}` will be incorrectly interpreted as `{{ ' }}` + `' }}`, i.e.
|
||||
* an interpolated expression consisting of a single-quote (`'`) and the `' }}` string.
|
||||
*
|
||||
* @param {string} text The text with markup to interpolate.
|
||||
* @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
|
||||
* embedded expression in order to return an interpolation function. Strings with no
|
||||
@@ -232,19 +220,6 @@ function $InterpolateProvider() {
|
||||
* - `context`: evaluation context for all expressions embedded in the interpolated text
|
||||
*/
|
||||
function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
|
||||
// Provide a quick exit and simplified result function for text with no interpolation
|
||||
if (!text.length || text.indexOf(startSymbol) === -1) {
|
||||
var constantInterp;
|
||||
if (!mustHaveExpression) {
|
||||
var unescapedText = unescapeText(text);
|
||||
constantInterp = valueFn(unescapedText);
|
||||
constantInterp.exp = text;
|
||||
constantInterp.expressions = [];
|
||||
constantInterp.$$watchDelegate = constantWatchDelegate;
|
||||
}
|
||||
return constantInterp;
|
||||
}
|
||||
|
||||
allOrNothing = !!allOrNothing;
|
||||
var startIndex,
|
||||
endIndex,
|
||||
|
||||