Compare commits
329 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 202aed7770 | |||
| 271572c20e | |||
| 550ba01b32 | |||
| 22948807e3 | |||
| b9007df590 | |||
| 0604bb7b7a | |||
| 92576743ee | |||
| c9b0bfecc9 | |||
| 2ae10f67fc | |||
| c2aaddbe4b | |||
| 6f7018d52f | |||
| 4f9ac078d5 | |||
| 9057ed21ac | |||
| c09f619318 | |||
| 1eda18365a | |||
| 77ce5b89f9 | |||
| c5f1ca3d91 | |||
| 7a36d49533 | |||
| cd21602d5b | |||
| 5f3f25a1a6 | |||
| cb73a37c7c | |||
| 49455a75dc | |||
| 85880a6490 | |||
| 46343c603d | |||
| 3b5d75c021 | |||
| 719c747cd8 | |||
| 2137542e09 | |||
| ab878a6c03 | |||
| 474a0337bd | |||
| 97a1b399b7 | |||
| c6bde52006 | |||
| 563be7e879 | |||
| e0489abd8d | |||
| 2218e6f8cd | |||
| cec9ecf951 | |||
| 36a547b852 | |||
| b3ec730c42 | |||
| 2ab0d5d370 | |||
| ac68ee49c1 | |||
| be8ef25a5a | |||
| 41c1b8858f | |||
| fce8915f39 | |||
| d4dd5dfa18 | |||
| a1e5cd5fe3 | |||
| 3660fd0912 | |||
| 4bca4c44b9 | |||
| a70e2833ea | |||
| 3be00df495 | |||
| 2efe1c2e7b | |||
| d8a02f9987 | |||
| a4520a745d | |||
| fe697527b4 | |||
| ea6fc6e69c | |||
| e94d454b84 | |||
| 2ae4f40be1 | |||
| db044c408a | |||
| 0e44ac2de0 | |||
| 5a1a0c9622 | |||
| 4ebbd7e210 | |||
| 5f90340abb | |||
| cc6fc199f5 | |||
| 29f0b568de | |||
| 4739b1d9da | |||
| feed7d6944 | |||
| 9da8d63ef4 | |||
| 0462b688f9 | |||
| 1b331f3729 | |||
| a6bd4bc866 | |||
| 23da614043 | |||
| bf0f5502b1 | |||
| 2f4437b3a1 | |||
| d0b41890bf | |||
| 7ef2921cae | |||
| 5e15b11509 | |||
| 42f28751e0 | |||
| b728030be0 | |||
| 2e3a7fd3e9 | |||
| fdf9989f7c | |||
| addfc567fd | |||
| a9371a1875 | |||
| 8ac90357a6 | |||
| 525a8f851e | |||
| fc8d6d75ab | |||
| b93601e691 | |||
| 1a2caad922 | |||
| ffbd276d6d | |||
| 75c4cbf81f | |||
| 642af96c48 | |||
| 252e8b5733 | |||
| 0c4997f7d8 | |||
| d713ad1b66 | |||
| 066c049957 | |||
| 9352bdfdaf | |||
| b8b8411b15 | |||
| 693e846add | |||
| fdeaa74c6c | |||
| 09de7b5db4 | |||
| a7fb357fa1 | |||
| 09b298705f | |||
| b674003f41 | |||
| 787c5a76dc | |||
| dfbe69c45a | |||
| 1339c11e36 | |||
| a603e202cc | |||
| 0872388a1b | |||
| 36230194be | |||
| 14b3db369e | |||
| a9d227120d | |||
| 05791c0133 | |||
| 00d5fde49c | |||
| 39f6e229c2 | |||
| 8863b9d04c | |||
| d18b281976 | |||
| f02f7d9c15 | |||
| 7eae29e5ab | |||
| 94b5c9f00e | |||
| 5b77e30c1a | |||
| b7e82a33ee | |||
| 6fdaa3d5a6 | |||
| d250dd43a3 | |||
| b8e54ef572 | |||
| e3a2ecf0db | |||
| 63249a0aaf | |||
| 28b54bbfbf | |||
| 77d3e75446 | |||
| 1bdca93d70 | |||
| bf1a57ad48 | |||
| ece6ef479c | |||
| f7fde04160 | |||
| 472019fb32 | |||
| a17578ad3d | |||
| cc16340e88 | |||
| fecfc5b09f | |||
| 6f5fc557ad | |||
| 0d608d041f | |||
| b5714ce1df | |||
| 27d4a4f0ca | |||
| 5cec4612c4 | |||
| 08eb05583b | |||
| 2ef25887ef | |||
| f8f7a1df34 | |||
| b5f7970be5 | |||
| d05f27e274 | |||
| b0571517c5 | |||
| c093c43b1f | |||
| e1c85d1f98 | |||
| 5feb9ab0f8 | |||
| 22bece7311 | |||
| 65ec0ab70a | |||
| 6133c63214 | |||
| 187b1b8ef4 | |||
| b24e381427 | |||
| e3f26b5a6b | |||
| c1cb341c87 | |||
| 0605e612e1 | |||
| 645625cf34 | |||
| e822e9061c | |||
| 301463a2e2 | |||
| 7d96ab0d13 | |||
| 0a738ce176 | |||
| d208ba2544 | |||
| de3f238764 | |||
| d302ea0cfa | |||
| a09fa35641 | |||
| 36e35b2cb1 | |||
| 13d113c522 | |||
| 215d9545dc | |||
| bdd853cb83 | |||
| e58d65a520 | |||
| fbd48845e0 | |||
| abb17cce8b | |||
| 3e0a2e1f33 | |||
| 1e8698b33e | |||
| 503dcb0281 | |||
| bf2e238418 | |||
| 8b77161702 | |||
| 50e16a67bb | |||
| b9b1aa7797 | |||
| a1341223c0 | |||
| 99d70af11c | |||
| 6acea1152f | |||
| 33ab57948c | |||
| 31ed0af74b | |||
| 13112710e6 | |||
| 54fa16e45d | |||
| fb00210cb6 | |||
| fafbd49490 | |||
| bda673f8e7 | |||
| b678f314f1 | |||
| 550965f111 | |||
| 443b521e22 | |||
| e6a9f9e130 | |||
| 441ab5235c | |||
| 6b7b40af74 | |||
| edc586f911 | |||
| c7393093ff | |||
| b493c62f6b | |||
| 35134a0e23 | |||
| 8d933bf995 | |||
| 6251751ad7 | |||
| 8f10dca300 | |||
| 566f1015d2 | |||
| 274e9c4ddf | |||
| e9cd6dc055 | |||
| 960a841051 | |||
| 01b10d7a24 | |||
| 2a5dbbdc52 | |||
| 6c4d601ff6 | |||
| d1536e7c8b | |||
| a19131494c | |||
| a353ed834c | |||
| bf0e83732a | |||
| 0554c1aae4 | |||
| 3f0e642eef | |||
| 1a05daf5dc | |||
| b9389b26ba | |||
| 6bdaa4bc21 | |||
| bfd311174d | |||
| 1229334fbd | |||
| 77a1acc7fc | |||
| 56bd0378bf | |||
| 76e57a764f | |||
| f30e2d093b | |||
| b33716f42a | |||
| 2d678f1d0a | |||
| 01d81cdab3 | |||
| 37ba3b9493 | |||
| 33a815d557 | |||
| d09e3e923e | |||
| 5038bf79c6 | |||
| e4e7e940f6 | |||
| a001a417d5 | |||
| b3047b9dc9 | |||
| ce378f2582 | |||
| 1b6a107a54 | |||
| 8c98d6a637 | |||
| acad09efa6 | |||
| 07d528396e | |||
| 7df7d1a9bc | |||
| fcd76d27e5 | |||
| a390612d15 | |||
| 833e60a203 | |||
| 1bb4e87dd6 | |||
| 60c13a17a7 | |||
| 7085b2bcac | |||
| acf38b382b | |||
| 947532828e | |||
| 91d5640a2c | |||
| fc73256464 | |||
| 23bc92b17d | |||
| 48b34ddb79 | |||
| 477626d846 | |||
| 25a476ea09 | |||
| dd2a803f4f | |||
| 7884c25643 | |||
| c024f28217 | |||
| 108a69be17 | |||
| 52b77b6b89 | |||
| e98258184a | |||
| 0107bfcab6 | |||
| 9e7cb3c375 | |||
| 38db2d4cec | |||
| 41cb588514 | |||
| 1571950fe3 | |||
| 9836a2d696 | |||
| 97f230a983 | |||
| 9a2f8e1778 | |||
| d2f8f25af2 | |||
| 28540c804a | |||
| e329243df1 | |||
| cdc7db3f35 | |||
| 7fdd26e6f3 | |||
| 48568b49dc | |||
| f92f52e29d | |||
| 6e8bec6c83 | |||
| c306d251f4 | |||
| a00c9bca40 | |||
| 34781f18cb | |||
| 3625803349 | |||
| ca0f59e677 | |||
| 38678cb65a | |||
| e5c6b7017b | |||
| 3607c9822f | |||
| 90251138bd | |||
| c03ad24903 | |||
| ccc3255fea | |||
| 583f37df5e | |||
| 4152155c25 | |||
| 2e0a4e385c | |||
| 1f533ad485 | |||
| 46f755a0bc | |||
| 0daaab0341 | |||
| f684c21145 | |||
| 86340a59bf | |||
| c03b9e5ec4 | |||
| 683d722233 | |||
| fefb7eda40 | |||
| 37117ab53a | |||
| 11f5aeeee9 | |||
| 925b2080a0 | |||
| 8a27abae89 | |||
| f3a763fd2e | |||
| c54228fbe9 | |||
| 99a2f0aba7 | |||
| 469ea3384a | |||
| 1a2ed705d4 | |||
| d0de0ce557 | |||
| 88765d581d | |||
| d1df21e45e | |||
| 4d875faa22 | |||
| 289b6b4dac | |||
| c4c83ef756 | |||
| 38ad144c33 | |||
| df58874747 | |||
| e0adb9c452 | |||
| eff00ea98c | |||
| 92a10d843e | |||
| dfd228497b | |||
| 3b158aab22 | |||
| f309c4ceba | |||
| a54d4600b3 | |||
| b5f0721e3d | |||
| f6f469e909 | |||
| 0209b2e3b2 | |||
| 2ad1b4cb9c | |||
| 55c6ee39e3 | |||
| c94329a891 | |||
| 3d0dcf68c1 | |||
| aa7dbf0cf7 |
@@ -0,0 +1,21 @@
|
||||
# http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[src/ngLocale/**]
|
||||
insert_final_newline = false
|
||||
|
||||
[dropdown-toggle.js]
|
||||
trim_trailing_whitespace = false
|
||||
insert_final_newline = false
|
||||
|
||||
[htmlparser.js]
|
||||
insert_final_newline = false
|
||||
@@ -1,4 +1,5 @@
|
||||
/build/
|
||||
/benchpress-build/
|
||||
.DS_Store
|
||||
gen_docs.disable
|
||||
test.disable
|
||||
@@ -14,6 +15,7 @@ angular.js.tmproj
|
||||
angular.xcodeproj
|
||||
.idea
|
||||
.agignore
|
||||
.lvimrc
|
||||
libpeerconnection.log
|
||||
npm-debug.log
|
||||
/tmp/
|
||||
|
||||
+2
-2
@@ -24,8 +24,8 @@ install:
|
||||
- npm config set spin false
|
||||
# Log HTTP requests
|
||||
- npm config set loglevel http
|
||||
# Run npm install twice, because it is flaky.
|
||||
- npm install || npm install
|
||||
- time ./scripts/travis/npm-bundle-deps.sh
|
||||
- time npm install
|
||||
|
||||
before_script:
|
||||
- mkdir -p $LOGS_DIR
|
||||
|
||||
+896
@@ -1,3 +1,869 @@
|
||||
<a name="1.3.0-RC.0"></a>
|
||||
# 1.3.0-RC.0 sonic-boltification (2014-08-29)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:**
|
||||
- wait two until two digests are over until enabling animations
|
||||
([92576743](https://github.com/angular/angular.js/commit/92576743eec0cef5ffdd701b83f72a61e6489c3b),
|
||||
[#8844](https://github.com/angular/angular.js/issues/8844))
|
||||
- ensure guarded animations consider AJAX requests upon bootstrap
|
||||
([4bca4c44](https://github.com/angular/angular.js/commit/4bca4c44b95a7435722605a750804043f2960160),
|
||||
[#8275](https://github.com/angular/angular.js/issues/8275), [#5262](https://github.com/angular/angular.js/issues/5262))
|
||||
- use $timeout to handle the delay within staggering animations
|
||||
([23da6140](https://github.com/angular/angular.js/commit/23da614043fe5dcf0be132b86466eecb11c766a2),
|
||||
[#7228](https://github.com/angular/angular.js/issues/7228), [#7547](https://github.com/angular/angular.js/issues/7547), [#8297](https://github.com/angular/angular.js/issues/8297), [#8547](https://github.com/angular/angular.js/issues/8547))
|
||||
- **$browser:** detect changes to the browser url that happened in sync
|
||||
([3be00df4](https://github.com/angular/angular.js/commit/3be00df495f6eed3b3d9587ebab1fdd633e94e08),
|
||||
[#6976](https://github.com/angular/angular.js/issues/6976))
|
||||
- **$compile:** use the correct namespace for transcluded svg elements
|
||||
([cb73a37c](https://github.com/angular/angular.js/commit/cb73a37c7cae5cdebadf7b3ddd44c5a452495e4e),
|
||||
[#8808](https://github.com/angular/angular.js/issues/8808), [#8816](https://github.com/angular/angular.js/issues/8816))
|
||||
- **$location:** always resolve relative links in html5mode to `<base>` url
|
||||
([22948807](https://github.com/angular/angular.js/commit/22948807e324eb0b182b15b31045dc306a9f3231),
|
||||
[#8492](https://github.com/angular/angular.js/issues/8492), [#8172](https://github.com/angular/angular.js/issues/8172))
|
||||
- **$parse:** properly handle dots at the end of identifiers
|
||||
([8ac90357](https://github.com/angular/angular.js/commit/8ac90357a66ae0c62dbfe6db2c6eaf1d600ecc65),
|
||||
[#4613](https://github.com/angular/angular.js/issues/4613), [#4912](https://github.com/angular/angular.js/issues/4912), [#8559](https://github.com/angular/angular.js/issues/8559))
|
||||
- **Angular:** remove duplicate nodeName_ references
|
||||
([a4520a74](https://github.com/angular/angular.js/commit/a4520a745d917c77f1d12cdbce48272c643f7255))
|
||||
- **currencyFilter:** pass through null and undefined values
|
||||
([c2aaddbe](https://github.com/angular/angular.js/commit/c2aaddbe4b21348aab8c13a78cdd6aaee846ae4e),
|
||||
[#8605](https://github.com/angular/angular.js/issues/8605))
|
||||
- **docs:** don't throw exception on the 404 page
|
||||
([550ba01b](https://github.com/angular/angular.js/commit/550ba01b325fc29460030fc9c24fa00269dec2a9),
|
||||
[#8518](https://github.com/angular/angular.js/issues/8518))
|
||||
- **input:**
|
||||
- validate minlength/maxlength for non-string values
|
||||
([77ce5b89](https://github.com/angular/angular.js/commit/77ce5b89f97aa83c3eb1fe2e19375ef00a822015),
|
||||
[#7967](https://github.com/angular/angular.js/issues/7967), [#8811](https://github.com/angular/angular.js/issues/8811))
|
||||
- allow to use seconds in `input[time]` and `input[datetime-local]`
|
||||
([5f90340a](https://github.com/angular/angular.js/commit/5f90340abb78aa08dde4876328bcc00e46232e46))
|
||||
- use year 1970 instead of 1900 for `input[time]`
|
||||
([29f0b568](https://github.com/angular/angular.js/commit/29f0b568debab7810752969d363d337099e96cdc))
|
||||
- **ngBindHtml:** throw error if interpolation is used in expression
|
||||
([cd21602d](https://github.com/angular/angular.js/commit/cd21602d5b1650d8be373618cb7320d697e32c4d),
|
||||
[#8824](https://github.com/angular/angular.js/issues/8824))
|
||||
- **ngEventDirs:** execute `blur` and `focus` expression using `scope.$evalAsync`
|
||||
([719c747c](https://github.com/angular/angular.js/commit/719c747cd892ee933e7e414a7dc97e657b88317d),
|
||||
[#4979](https://github.com/angular/angular.js/issues/4979), [#5945](https://github.com/angular/angular.js/issues/5945), [#8803](https://github.com/angular/angular.js/issues/8803), [#6910](https://github.com/angular/angular.js/issues/6910), [#5402](https://github.com/angular/angular.js/issues/5402))
|
||||
- **ngModel:**
|
||||
- always format the viewValue as a string for text, url and email types
|
||||
([1eda1836](https://github.com/angular/angular.js/commit/1eda18365a348c9597aafba9d195d345e4f13d1e))
|
||||
- allow non-assignable binding when getterSetter is used
|
||||
([ab878a6c](https://github.com/angular/angular.js/commit/ab878a6c038f47b95f3a7e85a4fdb599e0c73e63),
|
||||
[#8704](https://github.com/angular/angular.js/issues/8704))
|
||||
- treat undefined parse responses as parse errors
|
||||
([db044c40](https://github.com/angular/angular.js/commit/db044c408a7f8082758b96ab739348810c36e15a))
|
||||
- **ngRepeat:** improve errors for duplicate items
|
||||
([0604bb7b](https://github.com/angular/angular.js/commit/0604bb7b7a6156e33679396e805e327662d9a178))
|
||||
- **ngSwitch:** avoid removing DOM nodes twice within watch operation
|
||||
([c9b0bfec](https://github.com/angular/angular.js/commit/c9b0bfecc99837af1c97792b3ca3408ba182b0bb),
|
||||
[#8662](https://github.com/angular/angular.js/issues/8662))
|
||||
- **numberFilter:** pass through null and undefined values
|
||||
([2ae10f67](https://github.com/angular/angular.js/commit/2ae10f67fcde3e172f695956301ef796b68a50c2),
|
||||
[#8605](https://github.com/angular/angular.js/issues/8605), [#8842](https://github.com/angular/angular.js/issues/8842))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **core:**
|
||||
- add angular.reloadWithDebugInfo()
|
||||
([41c1b88](https://github.com/angular/angular.js/commit/41c1b8858f02c7310bfabdd545ebb28e90eb4258))
|
||||
- **$animate:**
|
||||
- use promises instead of callbacks for animations
|
||||
([bf0f5502](https://github.com/angular/angular.js/commit/bf0f5502b1bbfddc5cdd2f138efd9188b8c652a9))
|
||||
- coalesce concurrent class-based animations within a digest loop
|
||||
([2f4437b3](https://github.com/angular/angular.js/commit/2f4437b3a149eafb899f25933bd6c713b167d10e))
|
||||
- **$compile:**
|
||||
- bind isolate scope properties to controller
|
||||
([5f3f25a1](https://github.com/angular/angular.js/commit/5f3f25a1a6f9d4f2a66e2700df3b9c5606f1c255),
|
||||
[#7635](https://github.com/angular/angular.js/issues/7635), [#7645](https://github.com/angular/angular.js/issues/7645))
|
||||
- allow disabling scope info
|
||||
([a1e5cd5f](https://github.com/angular/angular.js/commit/a1e5cd5fe3906ebee8c400247a1f793d3e2239fb))
|
||||
- **$compile/ngBind:** allow disabling binding info
|
||||
([3660fd09](https://github.com/angular/angular.js/commit/3660fd0912d3ccf6def8c9f02d8d4c0621c8d91f))
|
||||
- **$http:** implement mechanism for coalescing calls to $apply in $http
|
||||
([ea6fc6e6](https://github.com/angular/angular.js/commit/ea6fc6e69c2a2aa213c71ed4e917a0d54d064e4c),
|
||||
[#8736](https://github.com/angular/angular.js/issues/8736), [#7634](https://github.com/angular/angular.js/issues/7634), [#5297](https://github.com/angular/angular.js/issues/5297))
|
||||
- **$rootScope:** implement $applyAsync to support combining calls to $apply into a single digest.
|
||||
([e94d454b](https://github.com/angular/angular.js/commit/e94d454b840f6cc55a440741382b407836ad245b))
|
||||
- **$templateRequest:** introduce the $templateRequest service
|
||||
([a70e2833](https://github.com/angular/angular.js/commit/a70e2833ea276107b11aafea96ef4a6724ad4d83))
|
||||
- **filter:** allow to define the timezone for formatting dates
|
||||
([4739b1d9](https://github.com/angular/angular.js/commit/4739b1d9daebfd094b6181c5f2cb52ff71e31c61))
|
||||
- **filterFilter:** pass index to function predicate
|
||||
([46343c60](https://github.com/angular/angular.js/commit/46343c603db6192daf5303b92eb664749326c7e6),
|
||||
[#654](https://github.com/angular/angular.js/issues/654))
|
||||
- **input:** allow to define the timezone for parsing dates
|
||||
([cc6fc199](https://github.com/angular/angular.js/commit/cc6fc199f5abaacdf781aa03634337d776eb0fc9),
|
||||
[#8447](https://github.com/angular/angular.js/issues/8447))
|
||||
- **minErr:** allow specifying ErrorConstructor in minErr constructor
|
||||
([a6bd4bc8](https://github.com/angular/angular.js/commit/a6bd4bc866a18f860c7548fa1b3f6d4c2a953416))
|
||||
- **ngModel:** provide validation API functions for sync and async validations
|
||||
([2ae4f40b](https://github.com/angular/angular.js/commit/2ae4f40be1803d999ca2a8cc30ec17ff19ea6d86))
|
||||
- **ngRoute:** alias string as redirectTo property in .otherwise()
|
||||
([3b5d75c0](https://github.com/angular/angular.js/commit/3b5d75c021e21fa6ec4dc6c47b8eafa55680ea63),
|
||||
[#7794](https://github.com/angular/angular.js/issues/7794))
|
||||
- **testability:** add $$testability service
|
||||
([85880a64](https://github.com/angular/angular.js/commit/85880a64900fa22a61feb926bf52de0965332ca5))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$compile:**
|
||||
- add debug classes in compile phase
|
||||
([e0489abd](https://github.com/angular/angular.js/commit/e0489abd8d9e4971ae23cc38805a92d227d1f3a1))
|
||||
- only iterate over elements with link functions
|
||||
([fdf9989f](https://github.com/angular/angular.js/commit/fdf9989f7cf1ed81982a788b75a338ac33334571),
|
||||
[#8741](https://github.com/angular/angular.js/issues/8741))
|
||||
- **nodeName_:** simplify the code and reduce the number of DOM calls
|
||||
([5a1a0c96](https://github.com/angular/angular.js/commit/5a1a0c96220101b5e040f0755e5eb401e2c73f65))
|
||||
- **select:** execute render after $digest cycle
|
||||
([6f7018d5](https://github.com/angular/angular.js/commit/6f7018d52fa4f9f9c7fa8e3035317d1239efb20f),
|
||||
[#8825](https://github.com/angular/angular.js/issues/8825))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$location**: due to [22948807](https://github.com/angular/angular.js/commit/22948807e324eb0b182b15b31045dc306a9f3231)
|
||||
|
||||
#### since 1.2.0 and 1.3.0-beta.1
|
||||
|
||||
Angular now requires a `<base>` tag when html5 mode of `$location` is enabled. Reasoning:
|
||||
Using html5 mode without a `<base href="...">` tag makes relative links for images, links, ...
|
||||
relative to the current url if the browser supports
|
||||
the history API. However, if the browser does not support the history API Angular falls back to using the `#`,
|
||||
and then all those relative links would be broken.
|
||||
|
||||
The `<base>` tag is also needed when a deep url is loaded from the server, e.g. `http://server/some/page/url`.
|
||||
In that case, Angular needs to decide which part of the url is the base of the application, and which part
|
||||
is path inside of the application.
|
||||
|
||||
To summarize: Now all relative links are always relative to the `<base>` tag.
|
||||
|
||||
Exception (also a breaking change):
|
||||
Link tags whose `href` attribute starts with a `#` will only change the hash of the url, but nothing else
|
||||
(e.g. `<a href="#someAnchor">`). This is to make it easy to scroll to anchors inside a document.
|
||||
|
||||
Related to #6162
|
||||
Closes #8492
|
||||
|
||||
#### since 1.2.17 and 1.3.0-beta.10
|
||||
|
||||
In html5 mode without a `<base>` tag on older browser that don't support the history API
|
||||
relative paths were adding up. E.g. clicking on `<a href="page1">` and then on `<a href="page2">`
|
||||
would produce `$location.path()==='/page1/page2'. The code that introduced this behavior was removed
|
||||
and Angular now also requires a `<base>` tag to be present when using html5 mode.
|
||||
|
||||
Closes #8172, #8233
|
||||
|
||||
|
||||
- **ngInclude, ngMessage, ngView and directives that load templates**: due to [a70e2833](https://github.com/angular/angular.js/commit/a70e2833ea276107b11aafea96ef4a6724ad4d83)
|
||||
|
||||
Angular will now throw a $compile minErr each a template fails to download
|
||||
for ngView, directives and ngMessage template requests. This changes the former
|
||||
behavior of silently ignoring failed HTTP requests--or when the template itself
|
||||
is empty. Please ensure that all directive, ngView and ngMessage code now properly
|
||||
addresses this scenario. NgInclude is uneffected from this change.
|
||||
|
||||
|
||||
- **$animate**: due to [23da6140](https://github.com/angular/angular.js/commit/23da614043fe5dcf0be132b86466eecb11c766a2)
|
||||
|
||||
If any stagger code consisted of having BOTH transition staggers and delay staggers
|
||||
together then that will not work the same way. Angular will now instead choose
|
||||
the highest stagger delay value and set the timeout to wait for that before
|
||||
applying the active CSS class.
|
||||
|
||||
|
||||
- **$animate**: due to [bf0f5502](https://github.com/angular/angular.js/commit/bf0f5502b1bbfddc5cdd2f138efd9188b8c652a9)
|
||||
|
||||
Both the API for the cancallation method and the done callback for
|
||||
$animate animations is different. Instead of using a callback function
|
||||
for each of the $animate animation methods, a promise is used instead.
|
||||
|
||||
```js
|
||||
//before
|
||||
$animate.enter(element, container, null, callbackFn);
|
||||
|
||||
//after
|
||||
$animate.enter(element, container).then(callbackFn);
|
||||
```
|
||||
|
||||
The animation can now be cancelled via `$animate.cancel(promise)`.
|
||||
|
||||
```js
|
||||
//before
|
||||
var cancelFn = $animate.enter(element, container);
|
||||
cancelFn(); //cancels the animation
|
||||
|
||||
//after
|
||||
var promise = $animate.enter(element, container);
|
||||
$animate.cancel(promise); //cancels the animation
|
||||
```
|
||||
|
||||
keep in mind that you will still need to run $scope.$apply inside of the `then` callback
|
||||
to trigger a digest.
|
||||
|
||||
|
||||
- **$animate**: due to [2f4437b3](https://github.com/angular/angular.js/commit/2f4437b3a149eafb899f25933bd6c713b167d10e)
|
||||
|
||||
$animate.addClass, $animate.removeClass and $animate.setClass will no longer start the animation
|
||||
right after being called in the directive code. The animation will only commence once a digest
|
||||
has passed. This means that all animation-related testing code requires an extra digest to kick
|
||||
off the animation.
|
||||
|
||||
```js
|
||||
//before this fix
|
||||
$animate.addClass(element, 'super');
|
||||
expect(element).toHaveClass('super');
|
||||
|
||||
//now
|
||||
$animate.addClass(element, 'super');
|
||||
$rootScope.$digest();
|
||||
expect(element).toHaveClass('super');
|
||||
```
|
||||
|
||||
$animate will also tally the amount of times classes are added and removed and only animate
|
||||
the left over classes once the digest kicks in. This means that for any directive code that
|
||||
adds and removes the same CSS class on the same element then this may result in no animation
|
||||
being triggered at all.
|
||||
|
||||
```js
|
||||
$animate.addClass(element, 'klass');
|
||||
$animate.removeClass(element, 'klass');
|
||||
|
||||
$rootScope.$digest();
|
||||
|
||||
//nothing happens...
|
||||
```
|
||||
|
||||
|
||||
- **$compile/ngBind:** due to [3660fd09](https://github.com/angular/angular.js/commit/3660fd0912d3ccf6def8c9f02d8d4c0621c8d91f),
|
||||
|
||||
The value of `$binding` data property on an element is always an array now
|
||||
and the expressions do not include the curly braces `{{ ... }}`.
|
||||
|
||||
|
||||
- **currencyFilter:** due to [c2aaddbe](https://github.com/angular/angular.js/commit/c2aaddbe4b21348aab8c13a78cdd6aaee846ae4e),
|
||||
previously the currency filter would convert null and undefined values into empty string, after this change
|
||||
these values will be passed through.
|
||||
|
||||
Only cases when the currency filter is chained with another filter that doesn't expect null/undefined will be affected. This
|
||||
should be very rare.
|
||||
|
||||
This change will not change the visual output of the filter because the interpolation will convert the null/undefined to
|
||||
an empty string.
|
||||
|
||||
Closes #8605
|
||||
|
||||
|
||||
- **numberFilter:** due to [2ae10f67](https://github.com/angular/angular.js/commit/2ae10f67fcde3e172f695956301ef796b68a50c2),
|
||||
previously the number filter would convert null and undefined values into empty string, after this change
|
||||
these values will be passed through.
|
||||
|
||||
Only cases when the number filter is chained with another filter that doesn't expect null/undefined will be affected. This
|
||||
should be very rare.
|
||||
|
||||
This change will not change the visual output of the filter because the interpolation will convert the null/undefined to
|
||||
an empty string.
|
||||
|
||||
Closes #8605
|
||||
Closes #8842
|
||||
|
||||
|
||||
- **input:**
|
||||
- due to [77ce5b89](https://github.com/angular/angular.js/commit/77ce5b89f97aa83c3eb1fe2e19375ef00a822015),
|
||||
|
||||
NgModel.viewValue will always be used when rendering validations for `minlength` and `maxlength`.
|
||||
|
||||
Closes #7967
|
||||
Closes #8811
|
||||
|
||||
- **input:**
|
||||
- due to [29f0b568](https://github.com/angular/angular.js/commit/29f0b568debab7810752969d363d337099e96cdc),
|
||||
|
||||
|
||||
According to the HTML5 spec `input[time]` should create dates
|
||||
based on the year 1970 (used to be based on the year 1900).
|
||||
|
||||
Related to #8447.
|
||||
|
||||
|
||||
- **ngModel**: due to [db044c40](https://github.com/angular/angular.js/commit/db044c408a7f8082758b96ab739348810c36e15a)
|
||||
|
||||
Any parser code from before that returned an `undefined` value
|
||||
(or nothing at all) will now cause a parser failure. When this occurs
|
||||
none of the validators present in `$validators` will run until the parser
|
||||
error is gone. The error will be stored on `ngModel.$error`.
|
||||
|
||||
|
||||
|
||||
|
||||
- **ngEventDirs:** due to [719c747c](https://github.com/angular/angular.js/commit/719c747cd892ee933e7e414a7dc97e657b88317d),
|
||||
|
||||
The `blur` and `focus` event fire synchronously, also during DOM operations
|
||||
that remove elements. This lead to errors as the Angular model was not
|
||||
in a consistent state. See this [fiddle](http://jsfiddle.net/fq1dq5yb/) for a demo.
|
||||
|
||||
This change executes the expression of those events using
|
||||
`scope.$evalAsync` if an `$apply` is in progress, otherwise
|
||||
keeps the old behavior.
|
||||
|
||||
Fixes #4979
|
||||
Fixes #5945
|
||||
Closes #8803
|
||||
Closes #6910
|
||||
Closes #5402
|
||||
|
||||
|
||||
|
||||
<a name="1.2.23"></a>
|
||||
# 1.2.23 superficial-malady (2014-08-22)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$location:**
|
||||
- rewrite relative URI correctly if `path==='/'` in legacy html5Mode
|
||||
([c6e4defc](https://github.com/angular/angular.js/commit/c6e4defcb6ec1ff43e9590b8fe9601d9e9da445d),
|
||||
[#8684](https://github.com/angular/angular.js/issues/8684))
|
||||
- don't call `indexOf()` of undefined `href` attribute
|
||||
([74a7afcb](https://github.com/angular/angular.js/commit/74a7afcb31b2e2aef2d7a4c3e3cf29f320669b0e),
|
||||
[#7721](https://github.com/angular/angular.js/issues/7721), [#8681](https://github.com/angular/angular.js/issues/8681))
|
||||
- **$sanitize:** sanitize javascript urls with comments
|
||||
([4f387050](https://github.com/angular/angular.js/commit/4f3870500da6f6f0c1b1d20c70404996b1a39585),
|
||||
[#8274](https://github.com/angular/angular.js/issues/8274))
|
||||
- **Angular:** make Date comparison in equals() NaN-aware
|
||||
([98f60372](https://github.com/angular/angular.js/commit/98f603722d81046031ad4a10e0a49b692871c2b2),
|
||||
[#8650](https://github.com/angular/angular.js/issues/8650), [#8715](https://github.com/angular/angular.js/issues/8715))
|
||||
- **copy:** clear array destinations correctly for non-array sources
|
||||
([888b0f54](https://github.com/angular/angular.js/commit/888b0f5400c2357dcc91300d1a4e66e52a8d8801),
|
||||
[#8610](https://github.com/angular/angular.js/issues/8610), [#8702](https://github.com/angular/angular.js/issues/8702))
|
||||
- **input:**
|
||||
- use lowercase method to account for undefined type
|
||||
([456026ef](https://github.com/angular/angular.js/commit/456026eff12ad70fa27dd08ec6bddc63e0f3e604))
|
||||
- by default, do not trim `input[type=password]` values
|
||||
([ebece0bc](https://github.com/angular/angular.js/commit/ebece0bcb9d64e59beb1c9b3418bed25e50ceef4),
|
||||
[#8250](https://github.com/angular/angular.js/issues/8250), [#8230](https://github.com/angular/angular.js/issues/8230))
|
||||
- **linky:** handle quotes around email addresses
|
||||
([effc98fd](https://github.com/angular/angular.js/commit/effc98fdc91937ae0aca30bc53e34a3c29863cd6),
|
||||
[#8520](https://github.com/angular/angular.js/issues/8520))
|
||||
- **minErr:** encode btstrpd error input to strip angle brackets
|
||||
([aaf9c5e5](https://github.com/angular/angular.js/commit/aaf9c5e598996ab17bce9579c8bfe63628b6620e),
|
||||
[#8683](https://github.com/angular/angular.js/issues/8683))
|
||||
- **ngHref:** remove attribute when empty value instead of ignoring
|
||||
([ed56872b](https://github.com/angular/angular.js/commit/ed56872bb2c9c479f90a479f52e3d4ef9c80d0c7),
|
||||
[#2755](https://github.com/angular/angular.js/issues/2755))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **input:** due to [ebece0bc](https://github.com/angular/angular.js/commit/ebece0bcb9d64e59beb1c9b3418bed25e50ceef4),
|
||||
|
||||
Previously, `input[type=password]` would trim values by default, and would require an explicit ng-trim="false"
|
||||
to disable the trimming behaviour. After this CL, `ng-trim` no longer affects `input[type=password]`, and will
|
||||
never trim the password value.
|
||||
|
||||
Closes #8250
|
||||
Closes #8230
|
||||
|
||||
|
||||
|
||||
<a name="1.3.0-beta.19"></a>
|
||||
# 1.3.0-beta.19 rafter-ascension (2014-08-22)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- use the correct namespace for transcluded SVG elements
|
||||
([ffbd276d](https://github.com/angular/angular.js/commit/ffbd276d6def6ff35bfdb30553346e985f4a0de6),
|
||||
[#8716](https://github.com/angular/angular.js/issues/8716))
|
||||
- update the jQuery `.context` when an element is replaced by `replace:true` directive
|
||||
([f02f7d9c](https://github.com/angular/angular.js/commit/f02f7d9c15deea9c5d83212301e2a5e18223bbe5),
|
||||
[#8253](https://github.com/angular/angular.js/issues/8253), [#7900](https://github.com/angular/angular.js/issues/7900))
|
||||
- **$location:**
|
||||
- rewrite relative URI correctly if `path==='/'` in legacy html5Mode
|
||||
([d18b2819](https://github.com/angular/angular.js/commit/d18b2819768e467897dee7bc223876ca23ea71b1),
|
||||
[#8684](https://github.com/angular/angular.js/issues/8684))
|
||||
- don't call `indexOf()` of undefined `href` attribute
|
||||
([5b77e30c](https://github.com/angular/angular.js/commit/5b77e30c1ac49be7b079b82527a5631f68bac904),
|
||||
[#7721](https://github.com/angular/angular.js/issues/7721), [#8681](https://github.com/angular/angular.js/issues/8681))
|
||||
- **$parse:** remove unused variable declaration in generated getters
|
||||
([6acea115](https://github.com/angular/angular.js/commit/6acea1152f72a4026583897c67bea2839bc9e89e))
|
||||
- **$sanitize:** sanitize javascript urls with comments
|
||||
([b7e82a33](https://github.com/angular/angular.js/commit/b7e82a33eee03fc683f982c6ee13d15d88b07f67),
|
||||
[#8274](https://github.com/angular/angular.js/issues/8274))
|
||||
- **$watchGroup:** call listener once when the `watchExpressions` array is empty
|
||||
([bf0e8373](https://github.com/angular/angular.js/commit/bf0e83732aa02c7aa08d0ccdf122116235fcfa11))
|
||||
- **Angular:** make Date comparison in `equals()` `NaN`-aware
|
||||
([693e846a](https://github.com/angular/angular.js/commit/693e846add5089d0e516604ae4a109e445fd3664),
|
||||
[#8650](https://github.com/angular/angular.js/issues/8650), [#8715](https://github.com/angular/angular.js/issues/8715))
|
||||
- **Scope:** don't clear the phase when an exception is thrown from asyncQueue or watch
|
||||
([bf1a57ad](https://github.com/angular/angular.js/commit/bf1a57ad4822bb152fdd4d2fb54c0689e466481b))
|
||||
- **copy:** clear array destinations correctly for non-array sources
|
||||
([a603e202](https://github.com/angular/angular.js/commit/a603e202cc7e048c2ab6f12dee1cc8f277cf6f4f),
|
||||
[#8610](https://github.com/angular/angular.js/issues/8610), [#8702](https://github.com/angular/angular.js/issues/8702))
|
||||
- **forEach:** match behaviour of Array.prototype.forEach (ignore missing properties)
|
||||
([36230194](https://github.com/angular/angular.js/commit/36230194be8aa417b0af33d618060829a75c4c5f),
|
||||
[#8510](https://github.com/angular/angular.js/issues/8510), [#8522](https://github.com/angular/angular.js/issues/8522), [#8525](https://github.com/angular/angular.js/issues/8525))
|
||||
- **input:**
|
||||
- use lowercase method to account for undefined type
|
||||
([066c0499](https://github.com/angular/angular.js/commit/066c049957a8af2fe449040eca2f1cb499655e32))
|
||||
- by default, do not trim input[type=password] values
|
||||
([a7fb357f](https://github.com/angular/angular.js/commit/a7fb357fa122e0a056ce1de838a2dfaf1ebc2953),
|
||||
[#8250](https://github.com/angular/angular.js/issues/8250), [#8230](https://github.com/angular/angular.js/issues/8230))
|
||||
- **jQuery:** cooperate with other libraries monkey-patching jQuery.cleanData
|
||||
([b9389b26](https://github.com/angular/angular.js/commit/b9389b26ba2cf6aa70372fa32a7b28c62d174bf5),
|
||||
[#8471](https://github.com/angular/angular.js/issues/8471))
|
||||
- **jqLite:**
|
||||
- clone wrapNode in jqlite/wrap
|
||||
([77d3e754](https://github.com/angular/angular.js/commit/77d3e7544642396d868aa49b85f0c027e8057bd7),
|
||||
[#3860](https://github.com/angular/angular.js/issues/3860), [#4194](https://github.com/angular/angular.js/issues/4194))
|
||||
- revert the `ready()` optimization until jQuery does the same
|
||||
([1bdca93d](https://github.com/angular/angular.js/commit/1bdca93d708ce9441b26d00e564210755395edf7))
|
||||
- **linky:** handle quotes around email addresses
|
||||
([a9d22712](https://github.com/angular/angular.js/commit/a9d227120dc2d433372da415a450e56b783b57a0),
|
||||
[#8520](https://github.com/angular/angular.js/issues/8520))
|
||||
- **minErr:** encode btstrpd error input to strip angle brackets
|
||||
([0872388a](https://github.com/angular/angular.js/commit/0872388a1b88b8637fdb0fb1ebbee269bead0508),
|
||||
[#8683](https://github.com/angular/angular.js/issues/8683))
|
||||
- **ngRepeat:**
|
||||
- allow aliasAs identifiers which contain but do not match reserved words
|
||||
([d713ad1b](https://github.com/angular/angular.js/commit/d713ad1b6607389649fbb8d12ac103565b02a1d4),
|
||||
[#8729](https://github.com/angular/angular.js/issues/8729))
|
||||
- make allowed aliasAs expressions more strict
|
||||
([09b29870](https://github.com/angular/angular.js/commit/09b298705f74255aff55bb7e4ba200c4200d712d),
|
||||
[#8438](https://github.com/angular/angular.js/issues/8438), [#8440](https://github.com/angular/angular.js/issues/8440))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:**
|
||||
- use allOrNothing interpolation for ngAttr*
|
||||
([09de7b5d](https://github.com/angular/angular.js/commit/09de7b5db466498becb295ecf5c1d0a698b1512c),
|
||||
[#8376](https://github.com/angular/angular.js/issues/8376), [#8399](https://github.com/angular/angular.js/issues/8399))
|
||||
- **benchpress:** configure benchpress grunt task
|
||||
([6bdaa4bc](https://github.com/angular/angular.js/commit/6bdaa4bc213805a58f51e9f5285dfe03bb06ddc3))
|
||||
- **jqLite:** implement the `detach` method
|
||||
([1a05daf5](https://github.com/angular/angular.js/commit/1a05daf5dc67813528afdb88086766dc22b6c0df),
|
||||
[#5461](https://github.com/angular/angular.js/issues/5461))
|
||||
- **ngRoute:** add method for changing url params
|
||||
([77a1acc7](https://github.com/angular/angular.js/commit/77a1acc7fcad7a8a7d0376b33d38a8977372cfe2))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$compile:**
|
||||
- don't register $destroy callbacks on element-transcluded nodes
|
||||
([b5f7970b](https://github.com/angular/angular.js/commit/b5f7970be5950580bde4de0002a578daf3ae3aac))
|
||||
- refactor publicLinkFn to simplify the code and use 'for in' loop
|
||||
([645625cf](https://github.com/angular/angular.js/commit/645625cf349a4be57691a7bf418b2386b4c1a53d))
|
||||
- clone the nodeList during linking only if necessary
|
||||
([3e0a2e1f](https://github.com/angular/angular.js/commit/3e0a2e1f3367a5b4ae7d8de6cff559f522aacfba))
|
||||
- delay object initialization in nodeLinkFn
|
||||
([31ed0af7](https://github.com/angular/angular.js/commit/31ed0af74b0081906415dcefe5610e1217cc0c48))
|
||||
- optimize nodeLinkFn
|
||||
([35134a0e](https://github.com/angular/angular.js/commit/35134a0e237d193cd7d3995dacfdc6bf3e92635e))
|
||||
- optimize publicLinkFn
|
||||
([274e9c4d](https://github.com/angular/angular.js/commit/274e9c4ddfd64138d39fcf84047aabc3ccde2f0b))
|
||||
- **$interpolate:** do not keep empty separators
|
||||
([94b5c9f0](https://github.com/angular/angular.js/commit/94b5c9f00edff7fa631d09316ceb9c7fd4c6426a))
|
||||
- **$parse:**
|
||||
- don't bind filters to a context
|
||||
([8863b9d0](https://github.com/angular/angular.js/commit/8863b9d04c722b278fa93c5d66ad1e578ad6eb1f))
|
||||
- optimize filter implementation
|
||||
([ece6ef47](https://github.com/angular/angular.js/commit/ece6ef479c741f17fc217d743cad64c516dbed27))
|
||||
- speed up fn invocation for no args case
|
||||
([a17578ad](https://github.com/angular/angular.js/commit/a17578ad3db5d1375aec1d601055ab718eeafd10))
|
||||
- speed up fn invocation by optimizing arg collection
|
||||
([fecfc5b0](https://github.com/angular/angular.js/commit/fecfc5b09feb7e4079364013b0beb6bf204ade2a))
|
||||
- use no-proto maps as caches and avoid hasOwnProperty checks
|
||||
([d302ea0c](https://github.com/angular/angular.js/commit/d302ea0cfade2787d7cc500398b7dcd3e4eff945))
|
||||
- trim expression only if string
|
||||
([a1341223](https://github.com/angular/angular.js/commit/a1341223c084c8188671bb8d6ea1608490b66f9f))
|
||||
- **$rootScope:** do not use `Function::call` when not needed
|
||||
([7eae29e5](https://github.com/angular/angular.js/commit/7eae29e5ab478ccb7e02fee8311f8b99ea1d165d))
|
||||
- **Scope:**
|
||||
- optimize `$watchCollection` when used for watching objects
|
||||
([e822e906](https://github.com/angular/angular.js/commit/e822e9061c2a605649d91abbd641f757e2829275))
|
||||
- don't use forEach in
|
||||
([301463a2](https://github.com/angular/angular.js/commit/301463a2e249011d7cb696c6cf34254f8317a706))
|
||||
- watchCollection optimization
|
||||
([7d96ab0d](https://github.com/angular/angular.js/commit/7d96ab0d132d923ec3e3a212aaf9d79f1d4a02de))
|
||||
- exit $broadcast early if nobody is listening for the given event
|
||||
([a09fa356](https://github.com/angular/angular.js/commit/a09fa356416c033a52666f3becf00524ecff3a03))
|
||||
- use remove the need for the extra watch in $watchGroup
|
||||
([3f0e642e](https://github.com/angular/angular.js/commit/3f0e642eefcbbb315839c4456ba6ac029a7b8a20),
|
||||
[#8396](https://github.com/angular/angular.js/issues/8396))
|
||||
- **benchpress:** add benchpress node module and port over large table test
|
||||
([1229334f](https://github.com/angular/angular.js/commit/1229334fbd8c778e95785d6a5e5589099ce655f7))
|
||||
- **isObject:** use strict comparison
|
||||
([d208ba25](https://github.com/angular/angular.js/commit/d208ba254442649d35f96c76bcd9e47326ec59f3))
|
||||
- **jqLite:**
|
||||
- simplify jqLiteDealoc
|
||||
([f8f7a1df](https://github.com/angular/angular.js/commit/f8f7a1df34560222cb5d2e18d4be996f5553815a))
|
||||
- optimize event handler
|
||||
([d05f27e2](https://github.com/angular/angular.js/commit/d05f27e274c41c33eebf4fe8035715d3f6596069))
|
||||
- only take `str.split()` path when needed
|
||||
([187b1b8e](https://github.com/angular/angular.js/commit/187b1b8ef45babd86afa853dc9321cd23160096e),
|
||||
[#8648](https://github.com/angular/angular.js/issues/8648))
|
||||
- optimize `off()`
|
||||
([abb17cce](https://github.com/angular/angular.js/commit/abb17cce8b459e4646d1c2a2428b691c3d95fb4c))
|
||||
- refactor jqLiteExpandoStore to minimize access to expensive element.ng339 expando property
|
||||
([1e8698b3](https://github.com/angular/angular.js/commit/1e8698b33e61b1a196f05f42856a2da4590a10e1))
|
||||
- microoptimization in chaining fn
|
||||
([fafbd494](https://github.com/angular/angular.js/commit/fafbd494907a8c068d79415b7ba8f42f283be521))
|
||||
- don't use String#split in on() unless we need it
|
||||
([bda673f8](https://github.com/angular/angular.js/commit/bda673f8e785f299407c8c45887f37448a0f0192))
|
||||
- don't check isString many times in constructor
|
||||
([443b521e](https://github.com/angular/angular.js/commit/443b521e22f9ec7009b913a2fe78caee0a515e87))
|
||||
- optimize jqLiteAcceptsData method
|
||||
([b493c62f](https://github.com/angular/angular.js/commit/b493c62f6b3e4288f5dee7c8b5952e088c2e3329))
|
||||
- optimize `append()` and `after()`
|
||||
([8d933bf9](https://github.com/angular/angular.js/commit/8d933bf99520fe3936e33d3ee28fd37e574b99de))
|
||||
- don't register DOM listener for $destroy event
|
||||
([6251751a](https://github.com/angular/angular.js/commit/6251751ad7bc2f3621db538edb5a9d7313a4ce6d))
|
||||
- optimize event listener registration
|
||||
([566f1015](https://github.com/angular/angular.js/commit/566f1015d27118d259e0886910d6b73b3cb0eb10))
|
||||
- improve createEventHandler method by switching from forEach to for loop
|
||||
([e9cd6dc0](https://github.com/angular/angular.js/commit/e9cd6dc055cb7bd80ae9232d8985b2bc3999135e))
|
||||
- don't use `forEach` in `off()`
|
||||
([960a8410](https://github.com/angular/angular.js/commit/960a8410515b2d7d461d7c95e8a2ca3d75129087))
|
||||
- don't recreate the Node.contains polyfill
|
||||
([d1536e7c](https://github.com/angular/angular.js/commit/d1536e7c8bf60549096138d08953a43190c7b1a6))
|
||||
- speed up shallowCopy and special case Attributes cloning
|
||||
([54fa16e4](https://github.com/angular/angular.js/commit/54fa16e45d8769ce6708a28388326db0eea53c7e))
|
||||
- **ngBind:** bypass jquery/jqlite when setting text
|
||||
([0a738ce1](https://github.com/angular/angular.js/commit/0a738ce1760f38efe45e79aa133442be09b56803))
|
||||
- **ngRepeat:**
|
||||
- simplify code and remove duplicate array.length access
|
||||
([08eb0558](https://github.com/angular/angular.js/commit/08eb05583bf39c63fef43b4faf29c61360699c81))
|
||||
- optimize marking of nodes that are being removed via an animation
|
||||
([36e35b2c](https://github.com/angular/angular.js/commit/36e35b2cb17c5ff7c43746d9ac0a259f77ff494e))
|
||||
- use no-proto objects for blockMaps
|
||||
([13d113c5](https://github.com/angular/angular.js/commit/13d113c522f124b91a1fd8606c22bbd399abf121))
|
||||
- move work to compile fn
|
||||
([bdd853cb](https://github.com/angular/angular.js/commit/bdd853cb83839eef9901af164293611eaa23ee2c))
|
||||
- move updateScope fn to factory and reuse it for all repeaters
|
||||
([e58d65a5](https://github.com/angular/angular.js/commit/e58d65a520cfbc630cbfbc248479416777ca16b2))
|
||||
- clone boundary comment nodes
|
||||
([fbd48845](https://github.com/angular/angular.js/commit/fbd48845e0e88e9935f82fe4c9f686ad78b5d924))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$compile:**
|
||||
- due to [09de7b5d](https://github.com/angular/angular.js/commit/09de7b5db466498becb295ecf5c1d0a698b1512c),
|
||||
|
||||
|
||||
Now, `ng-attr-*` will never add the attribute to the DOM if any of the interpolated expressions
|
||||
evaluate to `undefined`.
|
||||
|
||||
To work around this, initialize values which are intended to be the empty string with the
|
||||
empty string:
|
||||
|
||||
For example, given the following markup:
|
||||
|
||||
```html
|
||||
<div ng-attr-style="border-radius: {{value}}{{units}}"></div>
|
||||
```
|
||||
|
||||
If `$scope.value` is `4`, and `$scope.units` is `undefined`, the resulting markup is unchanged:
|
||||
|
||||
```html
|
||||
<div ng-attr-style="border-radius: {{value}}{{units}}"></div>
|
||||
```
|
||||
|
||||
However, if $scope.units is `""`, then the resulting markup is updated:
|
||||
|
||||
```html
|
||||
<div ng-attr-style="border-radius: {{value}}{{units}}" style="border-radius: 4"></div>
|
||||
```
|
||||
|
||||
Closes #8376
|
||||
Closes #8399
|
||||
|
||||
- due to [0d608d04](https://github.com/angular/angular.js/commit/0d608d041f37a659d8d8ba7a9b688e132587035d),
|
||||
element-transcluded directives now have an extra comment automatically appended to their cloned DOM
|
||||
|
||||
This comment is usually needed to keep track the end boundary in the event child directives modify the root node(s).
|
||||
If not used for this purpose it can be safely ignored.
|
||||
|
||||
- due to [75c4cbf8](https://github.com/angular/angular.js/commit/75c4cbf81fcd6d49656d3cb044e59e5fd24e0479),
|
||||
`directive.type` was renamed to `directive.templateNamespace`
|
||||
|
||||
This change is breaking only within 1.3.0-beta releases: `directive.type` was renamed to `directive.templateNamespace`
|
||||
|
||||
The property name `type` was too general.
|
||||
|
||||
- **$parse:** due to [8863b9d0](https://github.com/angular/angular.js/commit/8863b9d04c722b278fa93c5d66ad1e578ad6eb1f),
|
||||
`this` in filters is now undefined and no longer the scope
|
||||
|
||||
It's a bad practice for filters to have hidden dependencies, so pulling stuff from scope directly
|
||||
is not a good idea. Scope being the filter context was never documented as public API, so we don't
|
||||
expect that any significant code depends on this behavior.
|
||||
|
||||
If an existing filter has a dependency on the scope instance, the scope reference can
|
||||
be passed into the filter as a filter argument (this is highly discouraged for new code):
|
||||
|
||||
Before: `{{ user.name | customFilter }}`
|
||||
After: `{{ user.name | customFilter:this }}`
|
||||
|
||||
- **Scope:** due to [0554c1aa](https://github.com/angular/angular.js/commit/0554c1aae49a81691154a77e70b602b0f24dca81),
|
||||
`deregisterNotifier` callback for `$watch` is no longer available
|
||||
|
||||
This API was available only in the last few 1.3 beta versions and is not
|
||||
very useful for applications, so we don't expect that anyone will be affected
|
||||
by this change.
|
||||
|
||||
- **input:** due to [a7fb357f](https://github.com/angular/angular.js/commit/a7fb357fa122e0a056ce1de838a2dfaf1ebc2953),
|
||||
by default, do not trim `input[type=password]` values.
|
||||
|
||||
Previously, `input[type=password]` would trim values by default, and would require an explicit `ng-trim="false"`
|
||||
to disable the trimming behaviour. After this change, `ng-trim` no longer affects `input[type=password]`, and will
|
||||
never trim the password value.
|
||||
|
||||
Closes #8250
|
||||
Closes #8230
|
||||
|
||||
|
||||
|
||||
<a name="1.3.0-beta.18"></a>
|
||||
# 1.3.0-beta.18 spontaneous-combustion (2014-08-12)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:** make '='-bindings NaN-aware
|
||||
([5038bf79](https://github.com/angular/angular.js/commit/5038bf79c6c8251d7449d887b44a4321e619c534),
|
||||
[#8553](https://github.com/angular/angular.js/issues/8553), [#8554](https://github.com/angular/angular.js/issues/8554))
|
||||
- **$location:** add semicolon to whitelist of delimiters to unencode
|
||||
([36258033](https://github.com/angular/angular.js/commit/3625803349de04f175f87a22cbb608738003811a),
|
||||
[#5019](https://github.com/angular/angular.js/issues/5019))
|
||||
- **$parse:**
|
||||
- one-time binding for literal expressions works as expected
|
||||
([c024f282](https://github.com/angular/angular.js/commit/c024f28217cf8eedd695dd4b933ecf2ba4243c15),
|
||||
[#8209](https://github.com/angular/angular.js/issues/8209))
|
||||
- correctly assign expressions who's path is undefined and that use brackets notation
|
||||
([c03ad249](https://github.com/angular/angular.js/commit/c03ad249033e701f3ad7aa358102e1cb87f5025c),
|
||||
[#8039](https://github.com/angular/angular.js/issues/8039))
|
||||
- **Scope:** add deregisterNotifier to oneTimeLiteralWatch signature
|
||||
([a001a417](https://github.com/angular/angular.js/commit/a001a417d5c12bad0fa09c88e045622b95239e2f))
|
||||
- **jqLite:**
|
||||
- allow `triggerHandler()` to accept custom event
|
||||
([01d81cda](https://github.com/angular/angular.js/commit/01d81cdab3dbbcb8b4204769eb5272096eb0837f),
|
||||
[#8469](https://github.com/angular/angular.js/issues/8469))
|
||||
- fix regression where mutating the dom tree on a event breaks jqLite.remove
|
||||
([a00c9bca](https://github.com/angular/angular.js/commit/a00c9bca401abe5b5b0a217be82333056422c811),
|
||||
[#8359](https://github.com/angular/angular.js/issues/8359))
|
||||
- **ngSanitize:** ensure `html` is a string in htmlParser()
|
||||
([34781f18](https://github.com/angular/angular.js/commit/34781f18cb75ded9ae29f4b78f5bacd079f76709),
|
||||
[#8417](https://github.com/angular/angular.js/issues/8417), [#8416](https://github.com/angular/angular.js/issues/8416))
|
||||
- **select:**
|
||||
- ensure that at least one option has the `selected` attribute set
|
||||
([25a476ea](https://github.com/angular/angular.js/commit/25a476ea096b200fb4f422aaa9cd7215e2596ad3),
|
||||
[#8366](https://github.com/angular/angular.js/issues/8366), [#8429](https://github.com/angular/angular.js/issues/8429))
|
||||
- do not update selected property of an option element on digest with no change event
|
||||
([cdc7db3f](https://github.com/angular/angular.js/commit/cdc7db3f35368a9175ed96c63f4bf56593fe1876),
|
||||
[#8221](https://github.com/angular/angular.js/issues/8221), [#7715](https://github.com/angular/angular.js/issues/7715))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$parse:** allow for assignments in ternary operator branches
|
||||
([2d678f1d](https://github.com/angular/angular.js/commit/2d678f1d0a3714fdd49e582b92787312af129947),
|
||||
[#8512](https://github.com/angular/angular.js/issues/8512), [#8484](https://github.com/angular/angular.js/issues/8484))
|
||||
- **form:** Add new $submitted state to forms
|
||||
([108a69be](https://github.com/angular/angular.js/commit/108a69be17df5884d026c57b2be3235c576250fe),
|
||||
[#8056](https://github.com/angular/angular.js/issues/8056))
|
||||
- **http:** allow caching for JSONP requests
|
||||
([3607c982](https://github.com/angular/angular.js/commit/3607c9822f57b4d01b3f09a6ae4efc7168bec6c5),
|
||||
[#1947](https://github.com/angular/angular.js/issues/1947), [#8356](https://github.com/angular/angular.js/issues/8356))
|
||||
- **jQuery:** upgrade to jQuery to 2.1.1
|
||||
([9e7cb3c3](https://github.com/angular/angular.js/commit/9e7cb3c37543008e6236bb5a2c4536df2e1e43a9))
|
||||
- **ngMock:** allow override of when/expect definitions
|
||||
([477626d8](https://github.com/angular/angular.js/commit/477626d846b4de65d1d5c7071e6a94361395ff42),
|
||||
[#5766](https://github.com/angular/angular.js/issues/5766), [#8352](https://github.com/angular/angular.js/issues/8352))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$q:** move Deferred and Promise methods to prototypes
|
||||
([23bc92b1](https://github.com/angular/angular.js/commit/23bc92b17df882a907fb326320f0622717fefe7b),
|
||||
[#8300](https://github.com/angular/angular.js/issues/8300))
|
||||
- **input:** prevent additional $digest when input is already touched
|
||||
([dd2a803f](https://github.com/angular/angular.js/commit/dd2a803f4f03ab629a51623c026d3e3f9dc9e91f),
|
||||
[#8450](https://github.com/angular/angular.js/issues/8450))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **jQuery:** due to [9e7cb3c3](https://github.com/angular/angular.js/commit/9e7cb3c37543008e6236bb5a2c4536df2e1e43a9),
|
||||
Angular no longer supports jQuery versions below 2.1.1.
|
||||
|
||||
|
||||
<a name="1.2.22"></a>
|
||||
# 1.2.22 finicky-pleasure (2014-08-12)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:** make '='-bindings NaN-aware
|
||||
([0b0acb03](https://github.com/angular/angular.js/commit/0b0acb03424a273965fa6e6175d584f53a90252c),
|
||||
[#8553](https://github.com/angular/angular.js/issues/8553), [#8554](https://github.com/angular/angular.js/issues/8554))
|
||||
- **$parse:** correctly assign expressions who's path is undefined and that use brackets notation
|
||||
([60366c8d](https://github.com/angular/angular.js/commit/60366c8d0bb5ffdd1bd8a8971820eb4868f3efd5),
|
||||
[#8039](https://github.com/angular/angular.js/issues/8039))
|
||||
- **jqLite:** allow `triggerHandler()` to accept custom event
|
||||
([d262378b](https://github.com/angular/angular.js/commit/d262378b7c047dcd925cf4b55b80c0697b292232),
|
||||
[#8469](https://github.com/angular/angular.js/issues/8469), [#8505](https://github.com/angular/angular.js/issues/8505))
|
||||
- **ngSanitize:** ensure `html` is a string in htmlParser()
|
||||
([9ee07551](https://github.com/angular/angular.js/commit/9ee075518f1ccec0f34aa49bd007aa2ed9a3b12e),
|
||||
[#8417](https://github.com/angular/angular.js/issues/8417), [#8416](https://github.com/angular/angular.js/issues/8416))
|
||||
- **select:**
|
||||
- ensure that at least one option has the `selected` attribute set
|
||||
([79538afd](https://github.com/angular/angular.js/commit/79538afd7bd730d49be8eb988a3a54848d8ddaec),
|
||||
[#8366](https://github.com/angular/angular.js/issues/8366), [#8429](https://github.com/angular/angular.js/issues/8429))
|
||||
- do not update selected property of an option element on digest with no change event
|
||||
([c2860944](https://github.com/angular/angular.js/commit/c2860944c61a0b910f703fe8a9717188ed387893),
|
||||
[#8221](https://github.com/angular/angular.js/issues/8221), [#7715](https://github.com/angular/angular.js/issues/7715))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$parse:** allow for assignments in ternary operator branches
|
||||
([93b0c2d8](https://github.com/angular/angular.js/commit/93b0c2d8925e354159cc421e5be1bca9582f7b70),
|
||||
[#8512](https://github.com/angular/angular.js/issues/8512), [#8484](https://github.com/angular/angular.js/issues/8484))
|
||||
- **http:** allow caching for JSONP requests
|
||||
([eab5731a](https://github.com/angular/angular.js/commit/eab5731afc788c59f3f2988db372299268df8614),
|
||||
[#1947](https://github.com/angular/angular.js/issues/1947), [#8356](https://github.com/angular/angular.js/issues/8356))
|
||||
|
||||
|
||||
<a name="1.3.0-beta.17"></a>
|
||||
# 1.3.0-beta.17 turing-autocompletion (2014-07-25)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **angular.copy:** clone regexp flags correctly
|
||||
([86340a59](https://github.com/angular/angular.js/commit/86340a59bf9eb7bdfc4f99000cecf628cd10d9c8),
|
||||
[#5781](https://github.com/angular/angular.js/issues/5781), [#8337](https://github.com/angular/angular.js/issues/8337))
|
||||
- **docs:** change plnkr form to open in same window
|
||||
([925b2080](https://github.com/angular/angular.js/commit/925b2080a0341d9348feeb4f492957a2e2c80082))
|
||||
- **jqLite:** triggerHandler support unbind self
|
||||
([8a27abae](https://github.com/angular/angular.js/commit/8a27abae896de3c4d94c407e8bb381e099d2d7f7),
|
||||
[#5984](https://github.com/angular/angular.js/issues/5984))
|
||||
- **ngHref:** remove attribute when empty value instead of ignoring
|
||||
([469ea338](https://github.com/angular/angular.js/commit/469ea3384ad48ca4765af807c0f41201edb527f9),
|
||||
[#2755](https://github.com/angular/angular.js/issues/2755))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:** change directive's restrict setting to default to EA (element/attribute)
|
||||
([11f5aeee](https://github.com/angular/angular.js/commit/11f5aeeee952a395edaf54e3277674f211a82fc7),
|
||||
[#8321](https://github.com/angular/angular.js/issues/8321))
|
||||
- **$q:** add streamlined ES6-style interface for using $q
|
||||
([f3a763fd](https://github.com/angular/angular.js/commit/f3a763fd2edd8a37b80c79a5aaa1444460cd2df7),
|
||||
[#8311](https://github.com/angular/angular.js/issues/8311), [#6427](https://github.com/angular/angular.js/issues/6427))
|
||||
- **ngRepeat:** provide support for aliasing filtered repeater results as a scope member
|
||||
([e0adb9c4](https://github.com/angular/angular.js/commit/e0adb9c452e172295209f785b62472688225fffb),
|
||||
[#5919](https://github.com/angular/angular.js/issues/5919), [#8046](https://github.com/angular/angular.js/issues/8046), [#8282](https://github.com/angular/angular.js/issues/8282))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$parse:** don't use reflective calls in generated functions
|
||||
([c54228fb](https://github.com/angular/angular.js/commit/c54228fbe9d42d8a3a159bf84dd1d2e99b259ece))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$compile:** due to [11f5aeee](https://github.com/angular/angular.js/commit/11f5aeeee952a395edaf54e3277674f211a82fc7),
|
||||
directives now match elements by default unless specific restriction rules are set via `restrict` property.
|
||||
|
||||
This means that if a directive 'myFoo' previously didn't specify matching restrictrion, it will now match both the attribute
|
||||
and element form.
|
||||
|
||||
Before:
|
||||
|
||||
`<div my-foo></div>` <---- my-foo attribute matched the directive
|
||||
|
||||
`<my-foo></my-foo>` <---- no match
|
||||
|
||||
After:
|
||||
|
||||
`<div my-foo></div>` <---- my-foo attribute matched the directive
|
||||
|
||||
`<my-foo></my-foo>` <---- my-foo element matched the directive
|
||||
|
||||
It is not expected that this will be a problem in practice because of widespread use of prefixes that make `<my-foo>` like
|
||||
elements unlikely.
|
||||
|
||||
Closes #8321
|
||||
|
||||
|
||||
<a name="1.2.21"></a>
|
||||
# 1.2.21 wizard-props (2014-07-25)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$http:** fix double-quoted date issue when encoding params
|
||||
([2f960f15](https://github.com/angular/angular.js/commit/2f960f1530ed936c57df612a352a0d996368f6a1),
|
||||
[#8150](https://github.com/angular/angular.js/issues/8150), [#6128](https://github.com/angular/angular.js/issues/6128), [#8154](https://github.com/angular/angular.js/issues/8154))
|
||||
- **$location:** handle plus character in query strings
|
||||
([60af504c](https://github.com/angular/angular.js/commit/60af504c18dbdde9dfe90e9a2badef6d9e798512),
|
||||
[#3042](https://github.com/angular/angular.js/issues/3042))
|
||||
- **$rootScope:** $watchCollection should handle NaN in objects
|
||||
([bf13d268](https://github.com/angular/angular.js/commit/bf13d2683d5880b18db00087e80ee0fd5e1f429a),
|
||||
[#7930](https://github.com/angular/angular.js/issues/7930))
|
||||
- **angular.copy:** clone regexp flags correctly
|
||||
([e25ed0d4](https://github.com/angular/angular.js/commit/e25ed0d48d9a1c577e78b1c96098841572c764ea),
|
||||
[#5781](https://github.com/angular/angular.js/issues/5781), [#8337](https://github.com/angular/angular.js/issues/8337))
|
||||
- **csp:** fix autodetection of CSP + better docs
|
||||
([0e5d3190](https://github.com/angular/angular.js/commit/0e5d31908e122f013427164f7bbeea914a9a5961),
|
||||
[#8162](https://github.com/angular/angular.js/issues/8162), [#8191](https://github.com/angular/angular.js/issues/8191))
|
||||
- **docs:** change plnkr form to open in same window
|
||||
([5d11e020](https://github.com/angular/angular.js/commit/5d11e02008731a78f302841863a83fe7ed3c37b9))
|
||||
- **jqLite:** triggerHandler support unbind self
|
||||
([209e6000](https://github.com/angular/angular.js/commit/209e60007042f7e8b34c54ec6bf7d6f703c0ba2a),
|
||||
[#5984](https://github.com/angular/angular.js/issues/5984))
|
||||
- **ngHref:** remove attribute when empty value instead of ignoring
|
||||
([948c86c6](https://github.com/angular/angular.js/commit/948c86c6025fca8e07921869d21cfac1c6333b05),
|
||||
[#2755](https://github.com/angular/angular.js/issues/2755))
|
||||
- **ngRoute:** remove unnecessary call to decodeURIComponent
|
||||
([1b779028](https://github.com/angular/angular.js/commit/1b779028fdd339febaa1fff5f3bd4cfcda46cc09),
|
||||
[#6326](https://github.com/angular/angular.js/issues/6326), [#6327](https://github.com/angular/angular.js/issues/6327))
|
||||
- **ngSanitize:**
|
||||
- follow HTML parser rules for start tags / allow < in text content
|
||||
([d175bb01](https://github.com/angular/angular.js/commit/d175bb01314efdcbad5c3cb31b02e298e26c6e19),
|
||||
[#8212](https://github.com/angular/angular.js/issues/8212), [#8193](https://github.com/angular/angular.js/issues/8193))
|
||||
- **orderBy:** correctly order by date values
|
||||
([f1b28847](https://github.com/angular/angular.js/commit/f1b28847c8123483e03ac2410de86fd33a80b5f4),
|
||||
[#6675](https://github.com/angular/angular.js/issues/6675), [#6746](https://github.com/angular/angular.js/issues/6746))
|
||||
- **select:** force visual update in IE
|
||||
([c0afbfac](https://github.com/angular/angular.js/commit/c0afbfaca57893403d8d4b0990879ad5b9ffc3e5),
|
||||
[#7692](https://github.com/angular/angular.js/issues/7692), [#8158](https://github.com/angular/angular.js/issues/8158))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$compile:** only create jqLite object when necessary
|
||||
([71eb1901](https://github.com/angular/angular.js/commit/71eb1901f6b9a3a6d4b772aa95ce0dc78ff847bc))
|
||||
- **$parse:** don't use reflective calls in generated functions
|
||||
([cbdf0c2a](https://github.com/angular/angular.js/commit/cbdf0c2afb9836ae4cca6d70cf555ff28f55a1d1))
|
||||
- **forEach:** use native for loop instead of forEach for Arrays
|
||||
([492b0cdf](https://github.com/angular/angular.js/commit/492b0cdf28d02f1d508455245b7d8e1d641d9f40))
|
||||
- **jqLite:** expose the low-level jqLite.data/removeData calls
|
||||
([3c46c943](https://github.com/angular/angular.js/commit/3c46c94342aa35131f3ba0f8f4a6b39338b87d56))
|
||||
- **ngBindHtml:** move addClass to the compile phase
|
||||
([8eede099](https://github.com/angular/angular.js/commit/8eede099cd8aa6d524d1de385d08432072fd294e),
|
||||
[#8261](https://github.com/angular/angular.js/issues/8261))
|
||||
|
||||
|
||||
<a name="1.3.0-beta.16"></a>
|
||||
# 1.3.0-beta.16 pizza-transubstantiation (2014-07-18)
|
||||
@@ -500,6 +1366,36 @@ Closes #3969
|
||||
Closes #4277
|
||||
Closes #7960
|
||||
|
||||
- **$timeout/$interval:**
|
||||
- due to [19b6b343](https://github.com/angular/angular.js/commit/19b6b3433ae9f8523cbc72ae97dbcf0c06960148)
|
||||
|
||||
|
||||
Previously, even if invokeApply was set to false, a $rootScope digest would occur during promise
|
||||
resolution. This is no longer the case, as promises returned from $timeout and $interval will no
|
||||
longer trigger $evalAsync (which in turn causes a $digest) if `invokeApply` is false.
|
||||
|
||||
Workarounds include manually triggering $scope.$apply(), or returning $q.defer().promise from a
|
||||
promise callback, and resolving or rejecting it when appropriate.
|
||||
|
||||
var interval = $interval(function() {
|
||||
if (someRequirementFulfilled) {
|
||||
$interval.cancel(interval);
|
||||
$scope.$apply();
|
||||
}
|
||||
}, 100, 0, false);
|
||||
|
||||
or:
|
||||
|
||||
var interval = $interval(function (idx) {
|
||||
// make the magic happen
|
||||
}, 1000, 10, false);
|
||||
interval.then(function(idx) {
|
||||
var deferred = $q.defer();
|
||||
// do the asynchronous magic --- $evalAsync will cause a digest and cause
|
||||
// bindings to update.
|
||||
return deferred.promise;
|
||||
});
|
||||
|
||||
<a name="1.2.19"></a>
|
||||
# 1.2.19 precognitive-flashbacks (2014-06-30)
|
||||
|
||||
|
||||
+2
-2
@@ -120,7 +120,7 @@ Before you submit your pull request consider the following guidelines:
|
||||
```
|
||||
|
||||
* In GitHub, send a pull request to `angular:master`.
|
||||
* If we suggest changes then
|
||||
* If we suggest changes then
|
||||
* Make the required updates.
|
||||
* Re-run the Angular test suite to ensure tests are still passing.
|
||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
@@ -238,7 +238,7 @@ reference GitHub issues that this commit **Closes**.
|
||||
|
||||
A detailed explanation can be found in this [document][commit-message-format].
|
||||
|
||||
## <a name="cla"></a> Signing the CLA
|
||||
## <a name="cla"></a> Signing the CLA
|
||||
|
||||
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code
|
||||
changes to be accepted, the CLA must be signed. It's a quick process, we promise!
|
||||
|
||||
+8
-2
@@ -10,6 +10,7 @@ module.exports = function(grunt) {
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.loadTasks('lib/grunt');
|
||||
grunt.loadNpmTasks('angular-benchpress');
|
||||
|
||||
var NG_VERSION = versionInfo.currentVersion;
|
||||
NG_VERSION.cdn = versionInfo.cdnVersion;
|
||||
@@ -22,7 +23,12 @@ module.exports = function(grunt) {
|
||||
//config
|
||||
grunt.initConfig({
|
||||
NG_VERSION: NG_VERSION,
|
||||
|
||||
bp_build: {
|
||||
options: {
|
||||
buildPath: 'build/benchmarks',
|
||||
benchmarksPath: 'benchmarks'
|
||||
}
|
||||
},
|
||||
parallel: {
|
||||
travis: {
|
||||
tasks: [
|
||||
@@ -161,7 +167,7 @@ module.exports = function(grunt) {
|
||||
scenario: {
|
||||
dest: 'build/angular-scenario.js',
|
||||
src: [
|
||||
'bower_components/jquery/jquery.js',
|
||||
'bower_components/jquery/dist/jquery.js',
|
||||
util.wrap([files['angularSrc'], files['angularScenario']], 'ngScenario/angular')
|
||||
],
|
||||
styles: {
|
||||
|
||||
+2
-2
@@ -61,9 +61,9 @@ This process based on the idea of minimizing user pain
|
||||
1. Label `origin: google` for issues from Google
|
||||
|
||||
1. Assign a milestone:
|
||||
* Backlog - triaged fixes and features, should be the default choice
|
||||
* Backlog - triaged fixes and features, should be the default choice
|
||||
* Current 1.x.y milestone (e.g. 1.3.0-beta-2) - regressions and urgent bugs only
|
||||
|
||||
|
||||
|
||||
1. Unassign yourself from the issue
|
||||
|
||||
|
||||
Vendored
+4
-2
@@ -34,6 +34,8 @@ var angularFiles = {
|
||||
'src/ng/sanitizeUri.js',
|
||||
'src/ng/sce.js',
|
||||
'src/ng/sniffer.js',
|
||||
'src/ng/templateRequest.js',
|
||||
'src/ng/testability.js',
|
||||
'src/ng/timeout.js',
|
||||
'src/ng/urlUtils.js',
|
||||
'src/ng/window.js',
|
||||
@@ -143,7 +145,7 @@ var angularFiles = {
|
||||
],
|
||||
|
||||
'karma': [
|
||||
'bower_components/jquery/jquery.js',
|
||||
'bower_components/jquery/dist/jquery.js',
|
||||
'test/jquery_remove.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
@@ -177,7 +179,7 @@ var angularFiles = {
|
||||
],
|
||||
|
||||
'karmaJquery': [
|
||||
'bower_components/jquery/jquery.js',
|
||||
'bower_components/jquery/dist/jquery.js',
|
||||
'test/jquery_alias.js',
|
||||
'@angularSrc',
|
||||
'src/publishExternalApis.js',
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
Instructions for using benchpress (how to create benchmarks, how to run, how to configure) can be
|
||||
found at: https://github.com/angular/benchpress/blob/master/README.md.
|
||||
|
||||
In this project, there is a configured grunt task for building the benchmarks,
|
||||
`grunt bp_build`, which places the runnable benchmarks in "/build/benchmarks/".
|
||||
The existing `grunt webserver` task can be used to serve the built benchmarks at `localhost:8000/build/benchmarks/<benchmark-name>`
|
||||
@@ -0,0 +1,57 @@
|
||||
var app = angular.module('eventDelegationBenchmark', []);
|
||||
|
||||
app.directive('noopDir', function() {
|
||||
return {
|
||||
compile: function($element, $attrs) {
|
||||
return function($scope, $element) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
app.directive('nativeClick', ['$parse', function($parse) {
|
||||
return {
|
||||
compile: function($element, $attrs) {
|
||||
var expr = $parse($attrs.tstEvent);
|
||||
return function($scope, $element) {
|
||||
$element[0].addEventListener('click', function() {
|
||||
console.log('clicked');
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
||||
app.directive('dlgtClick', function() {
|
||||
return {
|
||||
compile: function($element, $attrs) {
|
||||
var evt = $attrs.dlgtClick;
|
||||
// We don't setup the global event listeners as the costs are small and one time only...
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
app.controller('DataController', function($rootScope) {
|
||||
this.ngRepeatCount = 1000;
|
||||
this.rows = [];
|
||||
var self = this;
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: '$apply',
|
||||
fn: function() {
|
||||
var oldRows = self.rows;
|
||||
$rootScope.$apply(function() {
|
||||
self.rows = [];
|
||||
});
|
||||
self.rows = oldRows;
|
||||
if (self.rows.length !== self.ngRepeatCount) {
|
||||
self.rows = [];
|
||||
for (var i=0; i<self.ngRepeatCount; i++) {
|
||||
self.rows.push('row'+i);
|
||||
}
|
||||
}
|
||||
$rootScope.$apply();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
scripts: [{
|
||||
id: 'angular',
|
||||
src: '/build/angular.js'
|
||||
},{
|
||||
src: 'app.js',
|
||||
}]
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,139 @@
|
||||
<div ng-app="eventDelegationBenchmark">
|
||||
<div ng-controller="DataController as ctrl">
|
||||
<div class="container-fluid">
|
||||
|
||||
<p>
|
||||
Impact of event delegation.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<label>
|
||||
Number of ngRepeats:
|
||||
<input type="number" ng-model="ctrl.ngRepeatCount">
|
||||
</label>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="ngClick">ngClick</label></div>
|
||||
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="ngClickNoJqLite">ngClick without jqLite</label></div>
|
||||
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="ngShow">baseline: ng-show</label></div>
|
||||
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="textInterpolation">baseline: text interpolation</label></div>
|
||||
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="dlgtClick">delegate event directive (only compile)</label></div>
|
||||
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="noopDir">baseline: noop directive (compile and link)</label></div>
|
||||
<div class="radio"><label><input type=radio ng-model="benchmarkType" value="noop">baseline: no directive</label></div>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
How to read the results:
|
||||
<ul>
|
||||
<li>The benchmark measures how long it takes to instantiate a given number of directives</li>
|
||||
<li>ngClick is compared against ngShow and text interpolation as baseline. The results show
|
||||
how expensive ngClick is compared to other very simple directives that touch the DOM.
|
||||
</li>
|
||||
<li>To measure the impact of jqLite.on vs element.addEventListener there is also a benchmark
|
||||
that as a modified version of ngClick that uses element.addEventListener.
|
||||
</li>
|
||||
<li>The delegate event directive is compared against a noop directive with a compile and link function and the case with no directives.
|
||||
The result shows how expensive it is to add a link function to a directive, as the delegate event directive has none.
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Results as of 7/31/2014:
|
||||
<ul>
|
||||
<li>ngClick is very close to ngShow and text interpolation, especially when looking at a version of ngClick that does not use jqLite.on but element.addEventListener instead.</li>
|
||||
<li>A delegate event directive that has no link function has the same speed as a directive with link function. I.e. ngClick is slower compared to the delegate event directive only because ngClick touches
|
||||
the DOM for every element</li>
|
||||
<li>A delegate event directive could be about 50% faster than ngClick. However, the overall performance
|
||||
benefit depends on how many (and which) other directives are used on the same element
|
||||
and what other things are part of the measures use case.
|
||||
E.g. rows of a table with ngRepeat that use ngClick will probably also contain text interpolation.
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
Debug output:
|
||||
<ng-switch on="benchmarkType">
|
||||
<div ng-switch-when="ngClick">
|
||||
<div>
|
||||
<span ng-repeat="row in ctrl.rows">
|
||||
<span ng-click="a()">1</span>
|
||||
<span ng-click="a()">1</span>
|
||||
<span ng-click="a()">1</span>
|
||||
<span ng-click="a()">1</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="ngClickNoJqLite">
|
||||
<div>
|
||||
<span ng-repeat="row in ctrl.rows">
|
||||
<span native-click="a()">1</span>
|
||||
<span native-click="a()">1</span>
|
||||
<span native-click="a()">1</span>
|
||||
<span native-click="a()">1</span>
|
||||
<span native-click="a()">1</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="ngShow">
|
||||
<div>
|
||||
<span ng-repeat="row in ctrl.rows">
|
||||
<span ng-show="true">1</span>
|
||||
<span ng-show="true">1</span>
|
||||
<span ng-show="true">1</span>
|
||||
<span ng-show="true">1</span>
|
||||
<span ng-show="true">1</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="textInterpolation">
|
||||
<div>
|
||||
<span ng-repeat="row in ctrl.rows">
|
||||
<span>{{row}}</span>
|
||||
<span>{{row}}</span>
|
||||
<span>{{row}}</span>
|
||||
<span>{{row}}</span>
|
||||
<span>{{row}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="dlgtClick">
|
||||
<div>
|
||||
<span ng-repeat="row in ctrl.rows">
|
||||
<span dlgt-click="a()">1</span>
|
||||
<span dlgt-click="a()">1</span>
|
||||
<span dlgt-click="a()">1</span>
|
||||
<span dlgt-click="a()">1</span>
|
||||
<span dlgt-click="a()">1</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="noopDir">
|
||||
<div>
|
||||
<span ng-repeat="row in ctrl.rows">
|
||||
<span noop-dir>1</span>
|
||||
<span noop-dir>1</span>
|
||||
<span noop-dir>1</span>
|
||||
<span noop-dir>1</span>
|
||||
<span noop-dir>1</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="noop">
|
||||
<div>
|
||||
<span ng-repeat="row in ctrl.rows">
|
||||
<span>1</span>
|
||||
<span>1</span>
|
||||
<span>1</span>
|
||||
<span>1</span>
|
||||
<span>1</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</ng-switch>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,183 @@
|
||||
var app = angular.module('largetableBenchmark', []);
|
||||
|
||||
app.config(function($compileProvider) {
|
||||
if ($compileProvider.debugInfoEnabled) {
|
||||
$compileProvider.debugInfoEnabled(false);
|
||||
}
|
||||
});
|
||||
|
||||
app.filter('noop', function() {
|
||||
return function(input) {
|
||||
return input;
|
||||
};
|
||||
});
|
||||
|
||||
app.controller('DataController', function($scope, $rootScope) {
|
||||
var totalRows = 1000;
|
||||
var totalColumns = 20;
|
||||
|
||||
var data = $scope.data = [];
|
||||
$scope.digestDuration = '?';
|
||||
$scope.numberOfBindings = totalRows*totalColumns*2 + totalRows + 1;
|
||||
$scope.numberOfWatches = '?';
|
||||
|
||||
function iGetter() { return this.i; }
|
||||
function jGetter() { return this.j; }
|
||||
|
||||
for (var i=0; i<totalRows; i++) {
|
||||
data[i] = [];
|
||||
for (var j=0; j<totalColumns; j++) {
|
||||
data[i][j] = {
|
||||
i: i, j: j,
|
||||
iFn: iGetter,
|
||||
jFn: jGetter
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var previousType;
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'destroy',
|
||||
fn: function() {
|
||||
$scope.$apply(function() {
|
||||
previousType = $scope.benchmarkType;
|
||||
$scope.benchmarkType = 'none';
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: 'create',
|
||||
fn: function() {
|
||||
$scope.$apply(function() {
|
||||
$scope.benchmarkType = previousType;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
benchmarkSteps.push({
|
||||
name: '$apply',
|
||||
fn: function() {
|
||||
$rootScope.$apply();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var fn = function() { return 'x'};
|
||||
|
||||
|
||||
app.directive('baselineBindingTable', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function ($scope, $element) {
|
||||
var i, j, row, cell, comment;
|
||||
var template = document.createElement('span');
|
||||
template.setAttribute('ng-repeat', 'foo in foos');
|
||||
template.classList.add('ng-scope');
|
||||
template.appendChild(document.createElement('span'));
|
||||
template.appendChild(document.createTextNode(':'));
|
||||
template.appendChild(document.createElement('span'));
|
||||
template.appendChild(document.createTextNode('|'));
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
row = document.createElement('div');
|
||||
$element[0].appendChild(row);
|
||||
for (j = 0; j < 20; j++) {
|
||||
cell = template.cloneNode(true);
|
||||
row.appendChild(cell);
|
||||
cell.childNodes[0].textContent = i;
|
||||
cell.childNodes[2].textContent = j;
|
||||
cell.ng3992 = 'xxx';
|
||||
comment = document.createComment('ngRepeat end: bar in foo');
|
||||
row.appendChild(comment);
|
||||
}
|
||||
|
||||
comment = document.createComment('ngRepeat end: foo in foos');
|
||||
$element[0].appendChild(comment);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
app.directive('baselineInterpolationTable', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
link: function ($scope, $element) {
|
||||
var i, j, row, cell, comment;
|
||||
var template = document.createElement('span');
|
||||
template.setAttribute('ng-repeat', 'foo in foos');
|
||||
template.classList.add('ng-scope');
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
row = document.createElement('div');
|
||||
$element[0].appendChild(row);
|
||||
for (j = 0; j < 20; j++) {
|
||||
cell = template.cloneNode(true);
|
||||
row.appendChild(cell);
|
||||
cell.textContent = '' + i + ':' + j + '|';
|
||||
cell.ng3992 = 'xxx';
|
||||
comment = document.createComment('ngRepeat end: bar in foo');
|
||||
row.appendChild(comment);
|
||||
}
|
||||
|
||||
comment = document.createComment('ngRepeat end: foo in foos');
|
||||
$element[0].appendChild(comment);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
||||
/*
|
||||
|
||||
the fastest
|
||||
240/44
|
||||
|
||||
app.directive('baselineTable', function() {
|
||||
return function($scope, $element) {
|
||||
var i, j, row, cell;
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
row = document.createElement('div');
|
||||
for (j = 0; j < 20; j++) {
|
||||
cell = document.createElement('span');
|
||||
cell.textContent = '' + i + ':' + j;
|
||||
row.appendChild(cell);
|
||||
}
|
||||
$element[0].appendChild(row);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
with comments and expando
|
||||
232/90
|
||||
|
||||
app.directive('baselineTable', function() {
|
||||
return function($scope, $element) {
|
||||
var i, j, row, cell, comment;
|
||||
|
||||
for (i = 0; i < 1000; i++) {
|
||||
row = document.createElement('div');
|
||||
$element[0].appendChild(row);
|
||||
for (j = 0; j < 20; j++) {
|
||||
cell = document.createElement('span');
|
||||
row.appendChild(cell);
|
||||
cell.textContent = '' + i + ':' + j;
|
||||
cell.ng3992 = 'xxx';
|
||||
comment = document.createComment('ngRepeat end: bar in foo');
|
||||
row.appendChild(comment);
|
||||
}
|
||||
|
||||
comment = document.createComment('ngRepeat end: foo in foos');
|
||||
$element[0].appendChild(comment);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
*/
|
||||
@@ -0,0 +1,15 @@
|
||||
module.exports = function(config) {
|
||||
config.set({
|
||||
scripts: [{
|
||||
id: 'jquery',
|
||||
src: 'jquery-noop.js'
|
||||
},
|
||||
{
|
||||
id: 'angular',
|
||||
src: '/build/angular.js'
|
||||
},
|
||||
{
|
||||
src: 'app.js',
|
||||
}]
|
||||
});
|
||||
};
|
||||
+1
@@ -0,0 +1 @@
|
||||
//Override me with ?jquery=/bower_components/jquery/dist/jquery.js
|
||||
@@ -0,0 +1,65 @@
|
||||
<style>
|
||||
[ng-cloak] { display: none; }
|
||||
</style>
|
||||
<div ng-app="largetableBenchmark" ng-cloak>
|
||||
<div ng-controller="DataController">
|
||||
<div class="container-fluid">
|
||||
<p>
|
||||
Large table rendered with AngularJS
|
||||
</p>
|
||||
|
||||
<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>interpolation: <input type=radio ng-model="benchmarkType" value="interpolation"></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>ngBind + filter: <input type=radio ng-model="benchmarkType" value="interpolationFilter"></div>
|
||||
|
||||
<ng-switch on="benchmarkType">
|
||||
<baseline-binding-table ng-switch-when="baselineBinding">
|
||||
</baseline-binding-table>
|
||||
<baseline-interpolation-table ng-switch-when="baselineInterpolation">
|
||||
</baseline-interpolation-table>
|
||||
<div ng-switch-when="ngBind">
|
||||
<h2>baseline binding</h2>
|
||||
<div ng-repeat="row in data">
|
||||
<span ng-repeat="column in row"><span ng-bind="column.i"></span>:<span ng-bind="column.j"></span>|</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="interpolation">
|
||||
<h2>baseline interpolation</h2>
|
||||
<div ng-repeat="row in data">
|
||||
<span ng-repeat="column in row">{{column.i}}:{{column.j}}|</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="ngBindFn">
|
||||
<h2>bindings with functions</h2>
|
||||
<div ng-repeat="row in data">
|
||||
<span ng-repeat="column in row"><span ng-bind="column.iFn()"></span>:<span ng-bind="column.jFn()"></span>|</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="interpolationFn">
|
||||
<h2>interpolation with functions</h2>
|
||||
<div ng-repeat="row in data">
|
||||
<span ng-repeat="column in row">{{column.iFn()}}:{{column.jFn()}}|</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="ngBindFilter">
|
||||
<h2>bindings with filter</h2>
|
||||
<div ng-repeat="row in data">
|
||||
<span ng-repeat="column in row"><span ng-bind="column.i | noop"></span>:<span ng-bind="column.j | noop"></span>|</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="interpolationFilter">
|
||||
<h2>interpolation with filter</h2>
|
||||
<div ng-repeat="row in data">
|
||||
<span ng-repeat="column in row">{{column.i | noop}}:{{column.j | noop}}|</span>
|
||||
</div>
|
||||
</div>
|
||||
</ng-switch>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+2
-2
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "AngularJS",
|
||||
"devDependencies": {
|
||||
"jquery": "1.10.2",
|
||||
"jquery": "2.1.1",
|
||||
"lunr.js": "0.4.3",
|
||||
"open-sans-fontface": "1.0.4",
|
||||
"google-code-prettify": "1.0.1",
|
||||
"closure-compiler": "https://closure-compiler.googlecode.com/files/compiler-20130603.zip",
|
||||
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
|
||||
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip",
|
||||
"bootstrap": "3.1.1"
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ then(allInSeries(function (branch) {
|
||||
line = line.split(' ');
|
||||
var sha = line.shift();
|
||||
var msg = line.join(' ');
|
||||
return sha + (msg.toLowerCase().indexOf('fix') === -1 ? ' ' : ' * ') + msg;
|
||||
return sha + ((/fix\([^\)]+\):/i.test(msg)) ? ' * ' : ' ') + msg;
|
||||
});
|
||||
branch.log = log.map(function (line) {
|
||||
return line.substr(41);
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.lang-text * {
|
||||
color: #333333!important;
|
||||
}
|
||||
|
||||
.pln {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ pre.prettyprint.linenums {
|
||||
}
|
||||
ol.linenums {
|
||||
margin: 0 0 0 33px; /* IE indents via margin-left */
|
||||
}
|
||||
}
|
||||
ol.linenums li {
|
||||
padding-left: 12px;
|
||||
font-size:12px;
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@ directive.runnableExample = ['$templateCache', '$document', function($templateCa
|
||||
'ng-repeat="tab in tabs track by $index" ' +
|
||||
'href="" ' +
|
||||
'class="btn"' +
|
||||
'ng-click="setTab($index)">' +
|
||||
'ng-click="setTab($index)">' +
|
||||
' {{ tab }}' +
|
||||
' </a>' +
|
||||
'</nav>';
|
||||
@@ -103,7 +103,7 @@ directive.syntax = function() {
|
||||
restrict: 'A',
|
||||
link: function(scope, element, attrs) {
|
||||
function makeLink(type, text, link, icon) {
|
||||
return '<a href="' + link + '" class="btn syntax-' + type + '" target="_blank" rel="nofollow">' +
|
||||
return '<a href="' + link + '" class="btn syntax-' + type + '" target="_blank" rel="nofollow">' +
|
||||
'<span class="' + icon + '"></span> ' + text +
|
||||
'</a>';
|
||||
};
|
||||
@@ -307,7 +307,7 @@ var popoverElement = function() {
|
||||
return this.titleElement.html(value);
|
||||
},
|
||||
|
||||
content : function(value) {
|
||||
content : function(value) {
|
||||
if(value && value.length > 0) {
|
||||
value = marked(value);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
var webdriver = require('protractor/node_modules/selenium-webdriver');
|
||||
|
||||
describe('docs.angularjs.org', function () {
|
||||
|
||||
beforeEach(function() {
|
||||
// read and clear logs from previous tests
|
||||
browser.manage().logs().get('browser');
|
||||
});
|
||||
|
||||
|
||||
afterEach(function() {
|
||||
// verify that there were no console errors in the browser
|
||||
browser.manage().logs().get('browser').then(function(browserLog) {
|
||||
var filteredLog = browserLog.filter(function(logEntry) {
|
||||
return logEntry.level.value > webdriver.logging.Level.WARNING.value;
|
||||
});
|
||||
expect(filteredLog.length).toEqual(0);
|
||||
if (filteredLog.length) {
|
||||
console.log('browser console errors: ' + require('util').inspect(filteredLog));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('App', function () {
|
||||
// it('should filter the module list when searching', function () {
|
||||
// browser.get();
|
||||
@@ -37,7 +60,7 @@ describe('docs.angularjs.org', function () {
|
||||
var nameInput = element(by.model('user.name'));
|
||||
nameInput.sendKeys('!!!');
|
||||
|
||||
var code = element(by.css('tt'));
|
||||
var code = element.all(by.css('tt')).first();
|
||||
expect(code.getText()).toContain('guest!!!');
|
||||
});
|
||||
|
||||
@@ -67,5 +90,28 @@ describe('docs.angularjs.org', function () {
|
||||
browser.get('index-debug.html#!error/ng/areq?p0=Missing&p1=not%20a%20function,%20got%20undefined');
|
||||
expect(element(by.css('.minerr-errmsg')).getText()).toEqual("Argument 'Missing' is not a function, got undefined");
|
||||
});
|
||||
|
||||
|
||||
it("should display links to code on GitHub", function() {
|
||||
browser.get('index-debug.html#!/api/does/not/exist');
|
||||
expect(element(by.css('h1')).getText()).toBe('Oops!');
|
||||
});
|
||||
});
|
||||
|
||||
describe("templates", function() {
|
||||
it("should show parameter defaults", function() {
|
||||
browser.get('index-debug.html#!/api/ng/service/$timeout');
|
||||
expect(element.all(by.css('.input-arguments p em')).first().getText()).toContain('(default: 0)');
|
||||
});
|
||||
});
|
||||
|
||||
describe("API pages", function() {
|
||||
it("should display links to code on GitHub", function() {
|
||||
browser.get('index-debug.html#!/api/ng/service/$http');
|
||||
expect(element(by.css('.improve-docs')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/edit\/.+\/src\/ng\/http\.js/);
|
||||
|
||||
browser.get('index-debug.html#!/api/ng/service/$http');
|
||||
expect(element(by.css('.view-source')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/tree\/.+\/src\/ng\/http\.js#L\d+/);
|
||||
});
|
||||
});
|
||||
});
|
||||
+1
-1
@@ -20,4 +20,4 @@ angular.module('docsApp', [
|
||||
|
||||
.config(function($locationProvider) {
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -33,7 +33,7 @@ angular.module('DocsController', [])
|
||||
|
||||
$scope.navClass = function(navItem) {
|
||||
return {
|
||||
active: navItem.href && this.currentPage.path,
|
||||
active: navItem.href && this.currentPage && this.currentPage.path,
|
||||
'nav-index-section': navItem.type === 'section'
|
||||
};
|
||||
};
|
||||
|
||||
@@ -59,4 +59,4 @@ angular.module('errors', ['ngSanitize'])
|
||||
element.html(errorLinkFilter(interpolate.apply(null, formatArgs), '_blank'));
|
||||
}
|
||||
};
|
||||
}]);
|
||||
}]);
|
||||
|
||||
@@ -2,7 +2,12 @@ angular.module('examples', [])
|
||||
|
||||
.factory('formPostData', ['$document', function($document) {
|
||||
return function(url, fields) {
|
||||
var form = angular.element('<form style="display: none;" method="post" action="' + url + '" target="_blank"></form>');
|
||||
/**
|
||||
* Form previously posted to target="_blank", but pop-up blockers were causing this to not work.
|
||||
* If a user chose to bypass pop-up blocker one time and click the link, they would arrive at
|
||||
* a new default plnkr, not a plnkr with the desired template.
|
||||
*/
|
||||
var form = angular.element('<form style="display: none;" method="post" action="' + url + '"></form>');
|
||||
angular.forEach(fields, function(value, name) {
|
||||
var input = angular.element('<input type="hidden" name="' + name + '">');
|
||||
input.attr('value', value);
|
||||
@@ -69,4 +74,4 @@ angular.module('examples', [])
|
||||
formPostData('http://plnkr.co/edit/?p=preview', postData);
|
||||
});
|
||||
};
|
||||
}]);
|
||||
}]);
|
||||
|
||||
@@ -21,4 +21,4 @@ angular.module('docsApp.navigationService', [])
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,31 @@
|
||||
"use strict";
|
||||
|
||||
angular.module('versions', [])
|
||||
|
||||
.controller('DocsVersionsCtrl', ['$scope', '$location', '$window', 'NG_VERSIONS', function($scope, $location, $window, NG_VERSIONS) {
|
||||
$scope.docs_versions = NG_VERSIONS;
|
||||
$scope.docs_version = NG_VERSIONS[0];
|
||||
|
||||
for(var i=0, minor = NaN; i < NG_VERSIONS.length; i++) {
|
||||
var version = NG_VERSIONS[i];
|
||||
// NaN will give false here
|
||||
if (minor <= version.minor) {
|
||||
continue;
|
||||
}
|
||||
version.isLatest = true;
|
||||
minor = version.minor;
|
||||
}
|
||||
|
||||
$scope.docs_versions = NG_VERSIONS;
|
||||
$scope.getGroupName = function(v) {
|
||||
return v.isLatest ? 'Latest' : (v.isStable ? 'Stable' : 'Unstable');
|
||||
};
|
||||
|
||||
$scope.jumpToDocsVersion = function(version) {
|
||||
var currentPagePath = $location.path();
|
||||
|
||||
// TODO: We need to do some munging of the path for different versions of the API...
|
||||
|
||||
|
||||
|
||||
$window.location = version.docsUrl + currentPagePath;
|
||||
};
|
||||
}]);
|
||||
}]);
|
||||
|
||||
@@ -31,4 +31,4 @@ describe("DocsController", function() {
|
||||
expect($window._gaq.pop()).toEqual(['_trackPageview', 'x/y/z']);
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ module.exports = function(config) {
|
||||
|
||||
config = basePackage(config);
|
||||
config = examplesPackage(config);
|
||||
|
||||
|
||||
config.append('processing.processors', [
|
||||
require('./processors/git-data'),
|
||||
require('./processors/error-docs'),
|
||||
|
||||
@@ -30,4 +30,4 @@ function writeFile(file, content) {
|
||||
return fs.makeTree(fs.directory(file)).then(function() {
|
||||
return fs.write(file, content, 'wb');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,4 +56,4 @@ module.exports = {
|
||||
|
||||
return docs.concat(_.values(errorNamespaces));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -17,4 +17,4 @@ module.exports = {
|
||||
process: function(extraData, gitData) {
|
||||
extraData.git = gitData;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -85,4 +85,4 @@ module.exports = {
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -33,7 +33,8 @@ module.exports = {
|
||||
innerTest: file.fileContents,
|
||||
pathPrefix: '.', // Hold for if we test with full jQuery
|
||||
exampleId: example.id,
|
||||
description: example.doc.id
|
||||
description: example.doc.id,
|
||||
'ng-app-included': example['ng-app-included']
|
||||
};
|
||||
|
||||
if (env === 'jquery') {
|
||||
|
||||
@@ -35,4 +35,4 @@ module.exports = {
|
||||
|
||||
docs.push(versionDoc);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,4 +14,4 @@
|
||||
{$ doc.description | marked $}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -25,4 +25,4 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
<div class="container main-grid main-header-grid">
|
||||
<div class="grid-left">
|
||||
<div ng-controller="DocsVersionsCtrl" class="picker version-picker">
|
||||
<select ng-options="v as ('v' + v.version + (v.isSnapshot ? ' (snapshot)' : '')) group by (v.isStable?'Stable':'Unstable') for v in docs_versions"
|
||||
<select ng-options="v as ('v' + v.version + (v.isSnapshot ? ' (snapshot)' : '')) group by getGroupName(v) for v in docs_versions"
|
||||
ng-model="docs_version"
|
||||
ng-change="jumpToDocsVersion(docs_version)"
|
||||
class="docs-version-jump">
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
describe("{$ doc.description $}", function() {
|
||||
var rootEl;
|
||||
beforeEach(function() {
|
||||
rootEl = browser.rootEl;{% if doc['ng-app-included'] %}
|
||||
browser.rootEl = '[ng-app]';{% endif %}
|
||||
browser.get("{$ doc.pathPrefix $}/{$ doc.examplePath $}");
|
||||
});
|
||||
|
||||
{% if doc['ng-app-included'] %}afterEach(function() { browser.rootEl = rootEl; });{% endif %}
|
||||
{$ doc.innerTest $}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,4 +24,4 @@
|
||||
</div>
|
||||
|
||||
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
|
||||
above is HTML and wrap each line in a <p> - thus breaking the HTML #}
|
||||
above is HTML and wrap each line in a <p> - thus breaking the HTML #}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{% include 'overview.template.html' %}
|
||||
{% include 'overview.template.html' %}
|
||||
|
||||
@@ -23,4 +23,4 @@ When an instance of `MyCtrl` is created, the service `myService` will be created
|
||||
by the `$injector`. `myService` depends on itself, which causes the `$injector`
|
||||
to detect a circular dependency and throw the error.
|
||||
|
||||
For more information, see the {@link guide/di Dependency Injection Guide}.
|
||||
For more information, see the {@link guide/di Dependency Injection Guide}.
|
||||
|
||||
@@ -23,4 +23,4 @@ To avoid the error, always use string literals for dependency injection annotati
|
||||
tokens.
|
||||
|
||||
For an explanation of what injection annotations are and how to use them, refer
|
||||
to the {@link guide/di Dependency Injection Guide}.
|
||||
to the {@link guide/di Dependency Injection Guide}.
|
||||
|
||||
@@ -23,4 +23,4 @@ angular.module("myApp", [])
|
||||
```
|
||||
|
||||
For more information, refer to the {@link auto.$provide#provider
|
||||
$provide.provider} api doc.
|
||||
$provide.provider} api doc.
|
||||
|
||||
@@ -14,7 +14,7 @@ angular.module('myApp', [])
|
||||
}]);
|
||||
```
|
||||
|
||||
The above code will fail with `$injector:unpr` if `myService` is not defined.
|
||||
The above code will fail with `$injector:unpr` if `myService` is not defined.
|
||||
|
||||
Making sure each dependency is defined will fix the problem, as noted below.
|
||||
|
||||
@@ -25,3 +25,33 @@ angular.module('myApp', [])
|
||||
// Do something with myService
|
||||
}]);
|
||||
```
|
||||
|
||||
An unknown provider error can also be caused by accidentally redefining a
|
||||
module using the `angular.module` API, as shown in the following example.
|
||||
|
||||
```
|
||||
angular.module('myModule', [])
|
||||
.service('myCoolService', function () { /* ... */ });
|
||||
|
||||
angular.module('myModule', [])
|
||||
// myModule has already been created! This is not what you want!
|
||||
.directive('myDirective', ['myCoolService', function (myCoolService) {
|
||||
// This directive definition throws unknown provider, because myCoolService
|
||||
// has been destroyed.
|
||||
}]);
|
||||
```
|
||||
|
||||
To fix this problem, make sure you only define each module with the
|
||||
`angular.module(name, [requires])` syntax once across your entire project.
|
||||
Retrieve it for subsequent use with `angular.module(name)`. The fixed example
|
||||
is shown below.
|
||||
|
||||
```
|
||||
angular.module('myModule', [])
|
||||
.service('myCoolService', function () { /* ... */ });
|
||||
|
||||
angular.module('myModule')
|
||||
.directive('myDirective', ['myCoolService', function (myCoolService) {
|
||||
// This directive definition does not throw unknown provider.
|
||||
}]);
|
||||
```
|
||||
@@ -9,4 +9,4 @@ it hard to reason about whether some combination of concatenated values are
|
||||
unsafe to use and could easily lead to XSS.
|
||||
|
||||
For more information about how AngularJS helps keep your app secure, refer to
|
||||
the {@link ng.$sce $sce} API doc.
|
||||
the {@link ng.$sce $sce} API doc.
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
@ngdoc error
|
||||
@name $location:nobase
|
||||
@fullName $location in HTML5 mode requires a <base> tag to be present!
|
||||
@description
|
||||
|
||||
If you configure {@link ng.$location `$location`} to use
|
||||
{@ng.provider.$locationProvider `html5Mode`} (`history.pushState`), you need to specify the base URL for the application with a [`<base href="">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag.
|
||||
|
||||
The base URL is then used to resolve all relative URLs throughout the application regardless of the
|
||||
entry point into the app.
|
||||
|
||||
If you are deploying your app into the root context (e.g. `https://myapp.com/`), set the base URL to `/`:
|
||||
|
||||
```html
|
||||
<head>
|
||||
<base href="/">
|
||||
...
|
||||
</head>
|
||||
```
|
||||
|
||||
If you are deploying your app into a sub-context (e.g. `https://myapp.com/subapp/`), set the base URL to the
|
||||
URL of the subcontext:
|
||||
|
||||
```html
|
||||
<head>
|
||||
<base href="/myapp">
|
||||
...
|
||||
</head>
|
||||
```
|
||||
|
||||
Before Angular 1.3 we didn't have this hard requirement and it was easy to write apps that worked
|
||||
when deployed in the root context but were broken when moved to a sub-context because in the
|
||||
sub-context all absolute urls would resolve to the root context of the app. To prevent this,
|
||||
use relative URLs throughout your app:
|
||||
|
||||
```html
|
||||
<!-- wrong: -->
|
||||
<a href="/userProfile">User Profile</a>
|
||||
|
||||
|
||||
<!-- correct: -->
|
||||
<a href="userProfile">User Profile</a>
|
||||
|
||||
```
|
||||
|
||||
Additionally, if you want to support [browsers that don't have the `history.pushState`
|
||||
API](http://caniuse.com/#feat=history), the fallback mechanism provided by `$location`
|
||||
won't work well without specifying the base url of the application.
|
||||
|
||||
In order to make it easier to migrate from hashbang mode to html5 mode, we require that the base
|
||||
URL is always specified when `$location`'s `html5mode` is enabled.
|
||||
@@ -14,4 +14,4 @@ Example expression that would result in this error:
|
||||
|
||||
```
|
||||
<div>{{user.sendInfo.call({}, true)}}</div>
|
||||
```
|
||||
```
|
||||
|
||||
@@ -24,4 +24,4 @@ Example expressions that would result in this error:
|
||||
<div>{{user.__proto__.hasOwnProperty = $emit}}</div>
|
||||
|
||||
<div>{{user.__defineGetter__('name', noop)}}</div>
|
||||
```
|
||||
```
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
@ngdoc error
|
||||
@name $q:norslvr
|
||||
@fullName No resolver function passed to $Q
|
||||
@description
|
||||
|
||||
Occurs when calling creating a promise using {@link $q} as a constructor, without providing the
|
||||
required `resolver` function.
|
||||
|
||||
```
|
||||
//bad
|
||||
var promise = $q().then(doSomething);
|
||||
|
||||
//good
|
||||
var promise = $q(function(resolve, reject) {
|
||||
waitForSomethingAsync.then(resolve);
|
||||
}).then(doSomething);
|
||||
```
|
||||
@@ -0,0 +1,23 @@
|
||||
@ngdoc error
|
||||
@name $q:qcycle
|
||||
@fullName Cannot resolve a promise with itself
|
||||
@description
|
||||
|
||||
Occurs when resolving a promise with itself as the value, including returning the promise in a
|
||||
function passed to `then`. The A+ 1.1 spec mandates that this behavior throw a TypeError.
|
||||
https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
|
||||
|
||||
```
|
||||
var promise = $q.defer().promise;
|
||||
|
||||
//bad
|
||||
promise.then(function (val) {
|
||||
//Cannot return self
|
||||
return promise;
|
||||
});
|
||||
|
||||
//good
|
||||
promise.then(function (val) {
|
||||
return 'some other value';
|
||||
});
|
||||
```
|
||||
@@ -5,4 +5,4 @@
|
||||
|
||||
Occurs when you try to use the name `hasOwnProperty` as a name of a parameter.
|
||||
Generally, a name cannot be `hasOwnProperty` because it is used, internally, on a object
|
||||
and allowing such a name would break lookups on this object.
|
||||
and allowing such a name would break lookups on this object.
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
|
||||
AngularJS often asserts that certain values will be present and truthy using a
|
||||
helper function. If the assertion fails, this error is thrown. To fix this problem,
|
||||
make sure that the value the assertion expects is defined and truthy.
|
||||
make sure that the value the assertion expects is defined and truthy.
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
|
||||
Occurs when you try to use the name `hasOwnProperty` in a context where it is not allow.
|
||||
Generally, a name cannot be `hasOwnProperty` because it is used, internally, on a object
|
||||
and allowing such a name would break lookups on this object.
|
||||
and allowing such a name would break lookups on this object.
|
||||
|
||||
@@ -49,4 +49,4 @@ You can also get this error if you accidentally load AngularJS itself more than
|
||||
<script src="angular.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
```
|
||||
|
||||
@@ -7,4 +7,4 @@ This error occurs when attempting to copy an object to itself. Calling {@link
|
||||
api/angular.copy angular.copy} with a `destination` object deletes
|
||||
all of the elements or properties on `destination` before copying to it. Copying
|
||||
an object to itself is not supported. Make sure to check your calls to
|
||||
`angular.copy` and avoid copying objects or arrays to themselves.
|
||||
`angular.copy` and avoid copying objects or arrays to themselves.
|
||||
|
||||
@@ -7,4 +7,4 @@ Copying Window or Scope instances is not supported because of cyclical and self
|
||||
references. Avoid copying windows and scopes, as well as any other cyclical or
|
||||
self-referential structures. Note that trying to deep copy an object containing
|
||||
cyclical references that is neither a window nor a scope will cause infinite
|
||||
recursion and a stack overflow.
|
||||
recursion and a stack overflow.
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
@ngdoc error
|
||||
@name ngRepeat:badident
|
||||
@fullName Invalid identifier expression
|
||||
@description
|
||||
|
||||
Occurs when an invalid identifier is specified in an {@link ng.directive:ngRepeat ngRepeat} expression.
|
||||
|
||||
The {@link ng.directive:ngRepeat ngRepeat} directive's `alias as` syntax is used to assign an alias for the processed collection in scope.
|
||||
|
||||
If the expression is not a simple identifier (such that you could declare it with `var {name}`, or if the expression is a reserved name,
|
||||
this error is thrown.
|
||||
|
||||
Reserved names include:
|
||||
|
||||
- `null`
|
||||
- `this`
|
||||
- `undefined`
|
||||
- `$parent`
|
||||
- `$even`
|
||||
- `$odd`
|
||||
- `$first`
|
||||
- `$last`
|
||||
- `$middle`
|
||||
|
||||
Invalid expressions might look like this:
|
||||
|
||||
```html
|
||||
<li ng-repeat="item in items | filter:searchString as this">{{item}}</li>
|
||||
<li ng-repeat="item in items | filter:searchString as some.objects["property"]">{{item}}</li>
|
||||
<li ng-repeat="item in items | filter:searchString as resultOfSomeMethod()">{{item}}</li>
|
||||
<li ng-repeat="item in items | filter:searchString as foo=6">{{item}}</li>
|
||||
```
|
||||
|
||||
Valid expressions might look like this:
|
||||
|
||||
```html
|
||||
<li ng-repeat="item in items | filter:searchString as collections">{{item}}</li>
|
||||
<li ng-repeat="item in items | filter:searchString as filteredCollection">{{item}}</li>
|
||||
```
|
||||
+299
-107
@@ -325,20 +325,22 @@ to URLs that should be handled with `.`. Now, links to locations, which are not
|
||||
are not prefixed with `.` and will not be intercepted by the `otherwise` rule in your `$routeProvider`.
|
||||
|
||||
|
||||
### Relative links
|
||||
|
||||
Be sure to check all relative links, images, scripts etc. Angular requires you to specify the url base in
|
||||
the head of your main html file (`<base href="/my-base">`). With that, relative urls will
|
||||
always be resolved to this base url, event if the initial url of the document was different.
|
||||
|
||||
There is one exception: Links that only contain a hash fragment (e.g. `<a href="#target">`)
|
||||
will only change `$location.hash()` and not modify the url otherwise. This is useful for scrolling
|
||||
to anchors on the same page without needing to know on which page the user currently is.
|
||||
|
||||
### Server side
|
||||
|
||||
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links
|
||||
to entry point of your application (e.g. index.html)
|
||||
|
||||
### Relative links
|
||||
|
||||
Be sure to check all relative links, images, scripts etc. You must either specify the url base in
|
||||
the head of your main html file (`<base href="/my-base">`) or you must use absolute urls
|
||||
(starting with `/`) everywhere because relative urls will be resolved to absolute urls using the
|
||||
initial absolute url of the document, which is often different from the root of the application.
|
||||
|
||||
Running Angular apps with the History API enabled from document root is strongly encouraged as it
|
||||
takes care of all relative link issues.
|
||||
to entry point of your application (e.g. index.html). Requiring a `<base>` tag is also important for
|
||||
this case, as it allows Angular to differentiate between the part of the url that is the application
|
||||
base and the path that should be handeled by the application.
|
||||
|
||||
### Sending links among different browsers
|
||||
|
||||
@@ -358,118 +360,308 @@ Note that when you type hashbang url into first browser (or vice versa) it doesn
|
||||
redirect to regular / hashbang url, as this conversion happens only during parsing the initial URL
|
||||
= on page reload.
|
||||
|
||||
In this examples we use `<base href="/base/index.html" />`
|
||||
<example>
|
||||
In these examples we use `<base href="/base/index.html" />`
|
||||
|
||||
#### Browser in HTML5 mode
|
||||
<example module="html5-mode" name="location-html5-mode">
|
||||
<file name="index.html">
|
||||
<div id="html5-mode" ng-controller="Html5Cntl">
|
||||
<h3>Browser with History API</h3>
|
||||
<div ng-address-bar browser="html5"></div><br><br>
|
||||
$location.protocol() = {{$location.protocol()}}<br>
|
||||
$location.host() = {{$location.host()}}<br>
|
||||
$location.port() = {{$location.port()}}<br>
|
||||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
|
||||
<div id="hashbang-mode" ng-controller="HashbangCntl">
|
||||
<h3>Browser without History API</h3>
|
||||
<div ng-address-bar browser="hashbang"></div><br><br>
|
||||
$location.protocol() = {{$location.protocol()}}<br>
|
||||
$location.host() = {{$location.host()}}<br>
|
||||
$location.port() = {{$location.port()}}<br>
|
||||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
<div ng-controller="LocationController">
|
||||
<div ng-address-bar></div><br><br>
|
||||
<div>
|
||||
$location.protocol() = <span ng-bind="$location.protocol()"></span> <br>
|
||||
$location.host() = <span ng-bind="$location.host()"></span> <br>
|
||||
$location.port() = <span ng-bind="$location.port()"></span> <br>
|
||||
$location.path() = <span ng-bind="$location.path()"></span> <br>
|
||||
$location.search() = <span ng-bind="$location.search()"></span> <br>
|
||||
$location.hash() = <span ng-bind="$location.hash()"></span> <br>
|
||||
</div>
|
||||
<div id="navigation">
|
||||
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="app.js">
|
||||
angular.module('html5-mode', ['fake-browser', 'address-bar'])
|
||||
|
||||
<file name="script.js">
|
||||
function FakeBrowser(initUrl, baseHref) {
|
||||
this.onUrlChange = function(fn) {
|
||||
this.urlChange = fn;
|
||||
.constant('initUrl', 'http://www.example.com/base/path?a=b#h')
|
||||
.constant('baseHref', '/base/index.html')
|
||||
.value('$sniffer', { history: true })
|
||||
|
||||
.controller("LocationController", function($scope, $location) {
|
||||
$scope.$location = {};
|
||||
angular.forEach("protocol host port path search hash".split(" "), function(method){
|
||||
$scope.$location[method] = function(){
|
||||
var result = $location[method].call($location);
|
||||
return angular.isObject(result) ? angular.toJson(result) : result;
|
||||
};
|
||||
});
|
||||
})
|
||||
|
||||
.config(function($locationProvider) {
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
})
|
||||
|
||||
.run(function($rootElement) {
|
||||
$rootElement.on('click', function(e) { e.stopPropagation(); });
|
||||
});
|
||||
</file>
|
||||
|
||||
<file name="fakeBrowser.js">
|
||||
angular.module('fake-browser', [])
|
||||
|
||||
.config(function($provide) {
|
||||
$provide.decorator('$browser', function($delegate, baseHref, initUrl) {
|
||||
|
||||
$delegate.onUrlChange = function(fn) {
|
||||
this.urlChange = fn;
|
||||
};
|
||||
|
||||
$delegate.url = function() {
|
||||
return initUrl;
|
||||
};
|
||||
|
||||
this.url = function() {
|
||||
return initUrl;
|
||||
};
|
||||
$delegate.defer = function(fn, delay) {
|
||||
setTimeout(function() { fn(); }, delay || 0);
|
||||
};
|
||||
|
||||
this.defer = function(fn, delay) {
|
||||
setTimeout(function() { fn(); }, delay || 0);
|
||||
};
|
||||
$delegate.baseHref = function() {
|
||||
return baseHref;
|
||||
};
|
||||
|
||||
this.baseHref = function() {
|
||||
return baseHref;
|
||||
};
|
||||
return $delegate;
|
||||
});
|
||||
});
|
||||
</file>
|
||||
|
||||
this.notifyWhenOutstandingRequests = angular.noop;
|
||||
}
|
||||
<file name="addressBar.js">
|
||||
angular.module('address-bar', [])
|
||||
.directive('ngAddressBar', function($browser, $timeout) {
|
||||
return {
|
||||
template: 'Address: <input id="addressBar" type="text" style="width: 400px" >',
|
||||
link: function(scope, element, attrs){
|
||||
var input = element.children("input"), delay;
|
||||
|
||||
var browsers = {
|
||||
html5: new FakeBrowser('http://www.example.com/base/path?a=b#h', '/base/index.html'),
|
||||
hashbang: new FakeBrowser('http://www.example.com/base/index.html#!/path?a=b#h', '/base/index.html')
|
||||
};
|
||||
input.on('keypress keyup keydown', function(event) {
|
||||
delay = (!delay ? $timeout(fireUrlChange, 250) : null);
|
||||
event.stopPropagation();
|
||||
})
|
||||
.val($browser.url());
|
||||
|
||||
function Html5Cntl($scope, $location) {
|
||||
$scope.$location = $location;
|
||||
}
|
||||
|
||||
function HashbangCntl($scope, $location) {
|
||||
$scope.$location = $location;
|
||||
}
|
||||
|
||||
function initEnv(name) {
|
||||
var root = angular.element(document.getElementById(name + '-mode'));
|
||||
// We must kill a link to the injector for this element otherwise angular will
|
||||
// complain that it has been bootstrapped already.
|
||||
root.data('$injector', null);
|
||||
angular.bootstrap(root, [function($compileProvider, $locationProvider, $provide){
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
|
||||
$provide.value('$browser', browsers[name]);
|
||||
$provide.value('$sniffer', {history: name == 'html5'});
|
||||
|
||||
$compileProvider.directive('ngAddressBar', function() {
|
||||
return function(scope, elm, attrs) {
|
||||
var browser = browsers[attrs.browser],
|
||||
input = angular.element('<input type="text" style="width: 400px">').val(browser.url()),
|
||||
delay;
|
||||
|
||||
input.on('keypress keyup keydown', function() {
|
||||
if (!delay) {
|
||||
delay = setTimeout(fireUrlChange, 250);
|
||||
}
|
||||
});
|
||||
|
||||
browser.url = function(url) {
|
||||
return input.val(url);
|
||||
};
|
||||
|
||||
elm.append('Address: ').append(input);
|
||||
|
||||
function fireUrlChange() {
|
||||
delay = null;
|
||||
browser.urlChange(input.val());
|
||||
}
|
||||
$browser.url = function(url) {
|
||||
return url ? input.val(url) : input.val();
|
||||
};
|
||||
});
|
||||
}]);
|
||||
root.on('click', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
}
|
||||
|
||||
initEnv('html5');
|
||||
initEnv('hashbang');
|
||||
function fireUrlChange() {
|
||||
delay = null;
|
||||
$browser.urlChange(input.val());
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
</file>
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
|
||||
var addressBar = element(by.css("#addressBar")),
|
||||
url = 'http://www.example.com/base/path?a=b#h';
|
||||
|
||||
|
||||
it("should show fake browser info on load", function(){
|
||||
expect(addressBar.getAttribute('value')).toBe(url);
|
||||
|
||||
expect(element(by.binding('$location.protocol()')).getText()).toBe('http');
|
||||
expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com');
|
||||
expect(element(by.binding('$location.port()')).getText()).toBe('80');
|
||||
expect(element(by.binding('$location.path()')).getText()).toBe('/path');
|
||||
expect(element(by.binding('$location.search()')).getText()).toBe('{"a":"b"}');
|
||||
expect(element(by.binding('$location.hash()')).getText()).toBe('h');
|
||||
|
||||
});
|
||||
|
||||
it("should change $location accordingly", function(){
|
||||
var navigation = element.all(by.css("#navigation a"));
|
||||
|
||||
navigation.get(0).click();
|
||||
|
||||
expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/first?a=b");
|
||||
|
||||
expect(element(by.binding('$location.protocol()')).getText()).toBe('http');
|
||||
expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com');
|
||||
expect(element(by.binding('$location.port()')).getText()).toBe('80');
|
||||
expect(element(by.binding('$location.path()')).getText()).toBe('/first');
|
||||
expect(element(by.binding('$location.search()')).getText()).toBe('{"a":"b"}');
|
||||
expect(element(by.binding('$location.hash()')).getText()).toBe('');
|
||||
|
||||
|
||||
navigation.get(1).click();
|
||||
|
||||
expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/sec/ond?flag#hash");
|
||||
|
||||
expect(element(by.binding('$location.protocol()')).getText()).toBe('http');
|
||||
expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com');
|
||||
expect(element(by.binding('$location.port()')).getText()).toBe('80');
|
||||
expect(element(by.binding('$location.path()')).getText()).toBe('/sec/ond');
|
||||
expect(element(by.binding('$location.search()')).getText()).toBe('{"flag":true}');
|
||||
expect(element(by.binding('$location.hash()')).getText()).toBe('hash');
|
||||
});
|
||||
|
||||
</file>
|
||||
|
||||
</example>
|
||||
|
||||
####Browser in HTML5 Fallback mode (Hashbang mode)
|
||||
<example module="hashbang-mode" name="location-hashbang-mode">
|
||||
<file name="index.html">
|
||||
<div ng-controller="LocationController">
|
||||
<div ng-address-bar></div><br><br>
|
||||
<div>
|
||||
$location.protocol() = <span ng-bind="$location.protocol()"></span> <br>
|
||||
$location.host() = <span ng-bind="$location.host()"></span> <br>
|
||||
$location.port() = <span ng-bind="$location.port()"></span> <br>
|
||||
$location.path() = <span ng-bind="$location.path()"></span> <br>
|
||||
$location.search() = <span ng-bind="$location.search()"></span> <br>
|
||||
$location.hash() = <span ng-bind="$location.hash()"></span> <br>
|
||||
</div>
|
||||
<div id="navigation">
|
||||
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="app.js">
|
||||
angular.module('hashbang-mode', ['fake-browser', 'address-bar'])
|
||||
|
||||
.constant('initUrl', 'http://www.example.com/base/index.html#!/path?a=b#h')
|
||||
.constant('baseHref', '/base/index.html')
|
||||
.value('$sniffer', { history: false })
|
||||
|
||||
.config(function($locationProvider) {
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
})
|
||||
|
||||
.controller("LocationController", function($scope, $location) {
|
||||
$scope.$location = {};
|
||||
angular.forEach("protocol host port path search hash".split(" "), function(method){
|
||||
$scope.$location[method] = function(){
|
||||
var result = $location[method].call($location);
|
||||
return angular.isObject(result) ? angular.toJson(result) : result;
|
||||
};
|
||||
});
|
||||
})
|
||||
|
||||
.run(function($rootElement) {
|
||||
$rootElement.on('click', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
});
|
||||
|
||||
</file>
|
||||
|
||||
<file name="fakeBrowser.js">
|
||||
angular.module('fake-browser', [])
|
||||
|
||||
.config(function($provide) {
|
||||
$provide.decorator('$browser', function($delegate, baseHref, initUrl) {
|
||||
|
||||
$delegate.onUrlChange = function(fn) {
|
||||
this.urlChange = fn;
|
||||
};
|
||||
|
||||
$delegate.url = function() {
|
||||
return initUrl;
|
||||
};
|
||||
|
||||
$delegate.defer = function(fn, delay) {
|
||||
setTimeout(function() { fn(); }, delay || 0);
|
||||
};
|
||||
|
||||
$delegate.baseHref = function() {
|
||||
return baseHref;
|
||||
};
|
||||
|
||||
return $delegate;
|
||||
});
|
||||
});
|
||||
</file>
|
||||
|
||||
|
||||
<file name="addressBar.js">
|
||||
angular.module('address-bar', [])
|
||||
.directive('ngAddressBar', function($browser, $timeout) {
|
||||
return {
|
||||
template: 'Address: <input id="addressBar" type="text" style="width: 400px" >',
|
||||
link: function(scope, element, attrs){
|
||||
var input = element.children("input"), delay;
|
||||
|
||||
input.on('keypress keyup keydown', function(event) {
|
||||
delay = (!delay ? $timeout(fireUrlChange, 250) : null);
|
||||
event.stopPropagation();
|
||||
})
|
||||
.val($browser.url());
|
||||
|
||||
$browser.url = function(url) {
|
||||
return url ? input.val(url) : input.val();
|
||||
};
|
||||
|
||||
function fireUrlChange() {
|
||||
delay = null;
|
||||
$browser.urlChange(input.val());
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
</file>
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
|
||||
var addressBar = element(by.css("#addressBar")),
|
||||
url = 'http://www.example.com/base/index.html#!/path?a=b#h';
|
||||
|
||||
it("should show fake browser info on load", function(){
|
||||
expect(addressBar.getAttribute('value')).toBe(url);
|
||||
|
||||
expect(element(by.binding('$location.protocol()')).getText()).toBe('http');
|
||||
expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com');
|
||||
expect(element(by.binding('$location.port()')).getText()).toBe('80');
|
||||
expect(element(by.binding('$location.path()')).getText()).toBe('/path');
|
||||
expect(element(by.binding('$location.search()')).getText()).toBe('{"a":"b"}');
|
||||
expect(element(by.binding('$location.hash()')).getText()).toBe('h');
|
||||
|
||||
});
|
||||
|
||||
it("should change $location accordingly", function(){
|
||||
var navigation = element.all(by.css("#navigation a"));
|
||||
|
||||
navigation.get(0).click();
|
||||
|
||||
expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/index.html#!/first?a=b");
|
||||
|
||||
expect(element(by.binding('$location.protocol()')).getText()).toBe('http');
|
||||
expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com');
|
||||
expect(element(by.binding('$location.port()')).getText()).toBe('80');
|
||||
expect(element(by.binding('$location.path()')).getText()).toBe('/first');
|
||||
expect(element(by.binding('$location.search()')).getText()).toBe('{"a":"b"}');
|
||||
expect(element(by.binding('$location.hash()')).getText()).toBe('');
|
||||
|
||||
|
||||
navigation.get(1).click();
|
||||
|
||||
expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/index.html#!/sec/ond?flag#hash");
|
||||
|
||||
expect(element(by.binding('$location.protocol()')).getText()).toBe('http');
|
||||
expect(element(by.binding('$location.host()')).getText()).toBe('www.example.com');
|
||||
expect(element(by.binding('$location.port()')).getText()).toBe('80');
|
||||
expect(element(by.binding('$location.path()')).getText()).toBe('/sec/ond');
|
||||
expect(element(by.binding('$location.search()')).getText()).toBe('{"flag":true}');
|
||||
expect(element(by.binding('$location.hash()')).getText()).toBe('hash');
|
||||
|
||||
});
|
||||
</file>
|
||||
|
||||
</example>
|
||||
|
||||
# Caveats
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ Here is an example of manually initializing Angular:
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
Hello {{'World'}}!
|
||||
Hello {{greetMe}}!
|
||||
<script src="http://code.angularjs.org/snapshot/angular.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -198,7 +198,7 @@ This should help give you an idea of what Angular does internally.
|
||||
|
||||
// Step 3: link the compiled template with the scope.
|
||||
var element = linkFn(scope);
|
||||
|
||||
|
||||
// Step 4: Append to DOM (optional)
|
||||
parent.appendChild(element);
|
||||
```
|
||||
|
||||
@@ -37,10 +37,10 @@ Let's start with input fields for quantity and cost whose values are multiplied
|
||||
<div ng-app ng-init="qty=1;cost=2">
|
||||
<b>Invoice:</b>
|
||||
<div>
|
||||
Quantity: <input type="number" ng-model="qty" required >
|
||||
Quantity: <input type="number" min="0" ng-model="qty">
|
||||
</div>
|
||||
<div>
|
||||
Costs: <input type="number" ng-model="cost" required >
|
||||
Costs: <input type="number" min="0" ng-model="cost">
|
||||
</div>
|
||||
<div>
|
||||
<b>Total:</b> {{qty * cost | currency}}
|
||||
@@ -62,11 +62,8 @@ The first kind of new markup are the so called <a name="directive">"{@link direc
|
||||
They apply special behavior to attributes or elements in the HTML. In the example above we use the
|
||||
{@link ng.directive:ngApp `ng-app`} attribute, which is linked to a directive that automatically
|
||||
initializes our application. Angular also defines a directive for the {@link ng.directive:input `input`}
|
||||
element that adds extra behavior to the element. E.g. it is able to automatically validate that the entered
|
||||
text is non empty by evaluating the `required` attribute.
|
||||
The {@link ng.directive:ngModel `ng-model`} directive stores/updates
|
||||
the value of the input field into/from a variable and shows the validation state of the input field by
|
||||
adding css classes. In the example we use these css classes to mark an empty input field with a red border.
|
||||
element that adds extra behavior to the element. The {@link ng.directive:ngModel `ng-model`} directive
|
||||
stores/updates the value of the input field into/from a variable.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Custom directives to access the DOM**: In Angular, the only place where an application touches the DOM is
|
||||
@@ -131,10 +128,10 @@ different currencies and also pay the invoice.
|
||||
<div ng-app="invoice1" ng-controller="InvoiceController as invoice">
|
||||
<b>Invoice:</b>
|
||||
<div>
|
||||
Quantity: <input type="number" ng-model="invoice.qty" required >
|
||||
Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
|
||||
</div>
|
||||
<div>
|
||||
Costs: <input type="number" ng-model="invoice.cost" required >
|
||||
Costs: <input type="number" min="0" ng-model="invoice.cost" required >
|
||||
<select ng-model="invoice.inCurr">
|
||||
<option ng-repeat="c in invoice.currencies">{{c}}</option>
|
||||
</select>
|
||||
@@ -231,10 +228,10 @@ Let's refactor our example and move the currency conversion into a service in an
|
||||
<div ng-app="invoice2" ng-controller="InvoiceController as invoice">
|
||||
<b>Invoice:</b>
|
||||
<div>
|
||||
Quantity: <input type="number" ng-model="invoice.qty" required >
|
||||
Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
|
||||
</div>
|
||||
<div>
|
||||
Costs: <input type="number" ng-model="invoice.cost" required >
|
||||
Costs: <input type="number" min="0" ng-model="invoice.cost" required >
|
||||
<select ng-model="invoice.inCurr">
|
||||
<option ng-repeat="c in invoice.currencies">{{c}}</option>
|
||||
</select>
|
||||
@@ -323,7 +320,7 @@ The following example shows how this is done with Angular:
|
||||
angular.module('finance3', [])
|
||||
.factory('currencyConverter', ['$http', function($http) {
|
||||
var YAHOO_FINANCE_URL_PATTERN =
|
||||
'http://query.yahooapis.com/v1/public/yql?q=select * from '+
|
||||
'//query.yahooapis.com/v1/public/yql?q=select * from '+
|
||||
'yahoo.finance.xchange where pair in ("PAIRS")&format=json&'+
|
||||
'env=store://datatables.org/alltableswithkeys&callback=JSON_CALLBACK';
|
||||
var currencies = ['USD', 'EUR', 'CNY'];
|
||||
@@ -359,10 +356,10 @@ The following example shows how this is done with Angular:
|
||||
<div ng-app="invoice3" ng-controller="InvoiceController as invoice">
|
||||
<b>Invoice:</b>
|
||||
<div>
|
||||
Quantity: <input type="number" ng-model="invoice.qty" required >
|
||||
Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
|
||||
</div>
|
||||
<div>
|
||||
Costs: <input type="number" ng-model="invoice.cost" required >
|
||||
Costs: <input type="number" min="0" ng-model="invoice.cost" required >
|
||||
<select ng-model="invoice.inCurr">
|
||||
<option ng-repeat="c in invoice.currencies">{{c}}</option>
|
||||
</select>
|
||||
|
||||
@@ -158,7 +158,7 @@ Things to notice in the example above:
|
||||
- The `ng-controller` directive is used to (implicitly) create a scope for our template, and the
|
||||
scope is augmented (managed) by the `SpicyController` Controller.
|
||||
- `SpicyController` is just a plain JavaScript function. As an (optional) naming convention the name
|
||||
starts with capital letter and ends with "Controller" or "Controller".
|
||||
starts with capital letter and ends with "Controller".
|
||||
- Assigning a property to `$scope` creates or updates the model.
|
||||
- Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method)
|
||||
- The Controller methods and properties are available in the template (for the `<div>` element and
|
||||
|
||||
@@ -109,7 +109,7 @@ asks the injector to create an instance of the controller and its dependencies.
|
||||
injector.instantiate(MyController);
|
||||
```
|
||||
|
||||
This is all done behinds the scenes. Notice that by having the `ng-controller` ask the injector to
|
||||
This is all done behind the scenes. Notice that by having the `ng-controller` ask the injector to
|
||||
instantiate the class, it can satisfy all of the dependencies of `MyController` without the
|
||||
controller ever knowing about the injector.
|
||||
|
||||
@@ -135,7 +135,7 @@ These can be used interchangeably as you see fit and are equivalent.
|
||||
|
||||
### Implicit Dependencies
|
||||
|
||||
The simplest way to get hold of the dependencies, is to assume that the function parameter names
|
||||
The simplest way to get hold of the dependencies is to assume that the function parameter names
|
||||
are the names of the dependencies.
|
||||
|
||||
```js
|
||||
@@ -144,7 +144,7 @@ function MyController($scope, greeter) {
|
||||
}
|
||||
```
|
||||
|
||||
Given a function the injector can infer the names of the service to inject by examining the
|
||||
Given a function the injector can infer the names of the services to inject by examining the
|
||||
function declaration and extracting the parameter names. In the above example `$scope`, and
|
||||
`greeter` are two services which need to be injected into the function.
|
||||
|
||||
@@ -154,7 +154,7 @@ rename the method parameter names. This makes this way of annotating only useful
|
||||
|
||||
### `$inject` Property Annotation
|
||||
|
||||
To allow the minifiers to rename the function parameters and still be able to inject right services,
|
||||
To allow the minifiers to rename the function parameters and still be able to inject the right services,
|
||||
the function needs to be annotated with the `$inject` property. The `$inject` property is an array
|
||||
of service names to inject.
|
||||
|
||||
@@ -166,7 +166,7 @@ MyController['$inject'] = ['$scope', 'greeter'];
|
||||
```
|
||||
|
||||
In this scenario the ordering of the values in the `$inject` array must match the ordering of the
|
||||
arguments to inject. Using above code snippet as an example, `$scope` will be injected into
|
||||
arguments to inject. Using the above code snippet as an example, `$scope` will be injected into
|
||||
`renamed$scope` and `greeter` into `renamedGreeter`. Care must be taken that the `$inject`
|
||||
annotation is kept in sync with the actual arguments in the function declaration.
|
||||
|
||||
@@ -206,7 +206,7 @@ someModule.factory('greeter', ['$window', function(renamed$window) {
|
||||
}]);
|
||||
```
|
||||
|
||||
Here, instead of simply providing the factory function, we pass an array, whose elements consist of
|
||||
Here, instead of simply providing the factory function, we pass an array whose elements consist of
|
||||
a list of strings (the names of the dependencies) followed by the function itself.
|
||||
|
||||
Keep in mind that all of the annotation styles are equivalent and can be used anywhere in Angular
|
||||
|
||||
@@ -84,7 +84,7 @@ Here are some equivalent examples of elements that match `ngBind`:
|
||||
<span x-ng-bind="name"></span> <br/>
|
||||
</div>
|
||||
</file>
|
||||
<file name="protractorTest.js">
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should show off bindings', function() {
|
||||
expect(element(by.css('div[ng-controller="Controller"] span[ng-bind]')).getText())
|
||||
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 – October 4, 1947)');
|
||||
@@ -161,7 +161,9 @@ With `ng-attr-cx` you can work around this problem.
|
||||
If an attribute with a binding is prefixed with the `ngAttr` prefix (denormalized as `ng-attr-`)
|
||||
then during the binding will be applied to the corresponding unprefixed attribute. This allows
|
||||
you to bind to attributes that would otherwise be eagerly processed by browsers
|
||||
(e.g. an SVG element's `circle[cx]` attributes).
|
||||
(e.g. an SVG element's `circle[cx]` attributes). When using `ngAttr`, the `allOrNothing` flag of
|
||||
{@link ng.$interpolate $interpolate} is used, so if any expression in the interpolated string
|
||||
results in `undefined`, the attribute is removed and not added to the element.
|
||||
|
||||
For example, we could fix the example above by instead writing:
|
||||
|
||||
@@ -283,8 +285,8 @@ Great! But what if we wanted to have our directive match the tag name `<my-custo
|
||||
If we simply put a `<my-customer>` element into the HTML, it doesn't work.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** When you create a directive, it is restricted to attribute only by default. In order to
|
||||
create directives that are triggered by element or class name, you need to use the `restrict` option.
|
||||
**Note:** When you create a directive, it is restricted to attribute and elements only by default. In order to
|
||||
create directives that are triggered by class name, you need to use the `restrict` option.
|
||||
</div>
|
||||
|
||||
The `restrict` option is typically set to:
|
||||
@@ -352,7 +354,7 @@ element as a customer component.
|
||||
Our `myCustomer` directive above is great, but it has a fatal flaw. We can only use it once within a
|
||||
given scope.
|
||||
|
||||
In its current implementation, we'd need to create a different controller each time In order to
|
||||
In its current implementation, we'd need to create a different controller each time in order to
|
||||
re-use such a directive:
|
||||
|
||||
<example module="docsScopeProblemExample">
|
||||
@@ -475,7 +477,6 @@ within our directive's template:
|
||||
angular.module('docsIsolationExample', [])
|
||||
.controller('Controller', ['$scope', function($scope) {
|
||||
$scope.naomi = { name: 'Naomi', address: '1600 Amphitheatre' };
|
||||
|
||||
$scope.vojta = { name: 'Vojta', address: '3456 Somewhere Else' };
|
||||
}])
|
||||
.directive('myCustomer', function() {
|
||||
@@ -537,7 +538,7 @@ where:
|
||||
In our `link` function, we want to update the displayed time once a second, or whenever a user
|
||||
changes the time formatting string that our directive binds to. We will use the `$interval` service
|
||||
to call a handler on a regular basis. This is easier than using `$timeout` but also works better with
|
||||
end-to-end testing, where we want to ensure that all $timeouts have completed before completing the test.
|
||||
end-to-end testing, where we want to ensure that all `$timeout`s have completed before completing the test.
|
||||
We also want to remove the `$interval` if the directive is deleted so we don't introduce a memory leak.
|
||||
|
||||
<example module="docsTimeDirective">
|
||||
@@ -910,6 +911,30 @@ Looking back at `myPane`'s definition, notice the last argument in its `link` fu
|
||||
When a directive requires a controller, it receives that controller as the fourth argument of its
|
||||
`link` function. Taking advantage of this, `myPane` can call the `addPane` function of `myTabs`.
|
||||
|
||||
If multiple controllers are required, the `require` option of the directive can take an array argument.
|
||||
The corresponding parameter being sent to the `link` function will also be an array.
|
||||
|
||||
```js
|
||||
angular.module('docsTabsExample', [])
|
||||
.directive('myPane', function() {
|
||||
return {
|
||||
require: ['^myTabs', '^ngModel'],
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: {
|
||||
title: '@'
|
||||
},
|
||||
link: function(scope, element, attrs, controllers) {
|
||||
var tabsCtrl = controllers[0],
|
||||
modelCtrl = controllers[1];
|
||||
|
||||
tabsCtrl.addPane(scope);
|
||||
},
|
||||
templateUrl: 'my-pane.html'
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
Savvy readers may be wondering what the difference is between `link` and `controller`.
|
||||
The basic difference is that `controller` can expose an API, and `link` functions can interact with
|
||||
controllers using `require`.
|
||||
|
||||
@@ -27,7 +27,7 @@ Protractor is a [Node.js](http://nodejs.org) program, and runs end to end tests
|
||||
written in JavaScript and run with node. Protractor uses [WebDriver](https://code.google.com/p/selenium/wiki/GettingStarted)
|
||||
to control browsers and simulate user actions.
|
||||
|
||||
For more information on Protractor, view [getting started](https://github.com/angular/protractor/blob/master/docs/overview.md)
|
||||
For more information on Protractor, view [getting started](https://github.com/angular/protractor/blob/master/docs/getting-started.md)
|
||||
or the [api docs](https://github.com/angular/protractor/blob/master/docs/api.md).
|
||||
|
||||
Protractor uses [Jasmine](http://jasmine.github.io/1.3/introduction.html) for its test syntax.
|
||||
|
||||
@@ -38,7 +38,9 @@ the method from your view. If you want to `eval()` an Angular expression yoursel
|
||||
## Example
|
||||
<example>
|
||||
<file name="index.html">
|
||||
1+2={{1+2}}
|
||||
<span>
|
||||
1+2={{1+2}}
|
||||
</span>
|
||||
</file>
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
|
||||
@@ -47,7 +47,7 @@ In Angular applications, you move the job of filling page templates with data fr
|
||||
|
||||
### Testing
|
||||
|
||||
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/dev_guide.unit-testing Unit testing}, {@link guide/dev_guide.services.testing_services Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
|
||||
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/unit-testing Unit testing}, {@link guide/services#unit-testing Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
|
||||
* **Scenario testing:** [Protractor](https://github.com/angular/protractor)
|
||||
|
||||
## Specific Topics
|
||||
@@ -81,6 +81,7 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
|
||||
### General
|
||||
|
||||
* **Docs Page:** {@link guide/production Running an AngularJS App in Production}
|
||||
* **Javascript minification: **[Background](http://thegreenpizza.github.io/2013/05/25/building-minification-safe-angular.js-applications/), [ng-annotate automation tool](https://github.com/olov/ng-annotate)
|
||||
* **Analytics and Logging:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Angulartics (Analytics)](https://github.com/luisfarzati/angulartics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
|
||||
* **SEO:** [By hand](http://www.yearofmoo.com/2012/11/angularjs-and-seo.html), [prerender.io](http://prerender.io/), [Brombone](http://www.brombone.com/), [SEO.js](http://getseojs.com/), [SEO4Ajax](http://www.seo4ajax.com/)
|
||||
|
||||
@@ -143,8 +143,8 @@ jQuery. We don't expect that app code actually depends on this accidental featur
|
||||
- **jqLite:** due to [d71dbb1a](https://github.com/angular/angular.js/commit/d71dbb1ae50f174680533492ce4c7db3ff74df00),
|
||||
the jQuery `detach()` method does not trigger the `$destroy` event.
|
||||
If you want to destroy Angular data attached to the element, use `remove()`.
|
||||
|
||||
|
||||
|
||||
|
||||
- **$http:** due to [ad4336f9](https://github.com/angular/angular.js/commit/ad4336f9359a073e272930f8f9bcd36587a8648f),
|
||||
|
||||
|
||||
@@ -756,8 +756,6 @@ See [80739409](https://github.com/angular/angular.js/commit/807394095b991357225a
|
||||
|
||||
## ngBindHtmlUnsafe has been removed and replaced by ngBindHtml
|
||||
|
||||
`ngBindHtml` which has been moved from `ngSanitize` module to the core `ng` module.
|
||||
|
||||
`ngBindHtml` provides `ngBindHtmlUnsafe` like
|
||||
behavior (evaluate an expression and innerHTML the result into the DOM) when bound to the result
|
||||
of `$sce.trustAsHtml(string)`. When bound to a plain string, the string is sanitized via
|
||||
@@ -765,6 +763,10 @@ of `$sce.trustAsHtml(string)`. When bound to a plain string, the string is sanit
|
||||
module is not loaded) and the bound expression evaluates to a value that is not trusted an
|
||||
exception is thrown.
|
||||
|
||||
When using this directive you can either include `ngSanitize` in your module's dependencis (See the
|
||||
example at the {@link ngBindHtml} reference) or use the {@link $sce} service to set the value as
|
||||
trusted.
|
||||
|
||||
See [dae69473](https://github.com/angular/angular.js/commit/dae694739b9581bea5dbc53522ec00d87b26ae55).
|
||||
|
||||
|
||||
|
||||
@@ -26,10 +26,12 @@ should be bootstrapped. There are several advantages to this approach:
|
||||
|
||||
I'm in a hurry. How do I get a Hello World module working?
|
||||
|
||||
<example module='myApp'>
|
||||
<example ng-app-included="true">
|
||||
<file name="index.html">
|
||||
<div>
|
||||
{{ 'World' | greet }}
|
||||
<div ng-app="myApp">
|
||||
<div>
|
||||
{{ 'World' | greet }}
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
@@ -48,7 +50,7 @@ I'm in a hurry. How do I get a Hello World module working?
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should add Hello to the name', function() {
|
||||
expect(element(by.binding("{{ 'World' | greet }}")).getText()).toEqual('Hello, World!');
|
||||
expect(element(by.binding("'World' | greet")).getText()).toEqual('Hello, World!');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
@@ -56,7 +58,7 @@ I'm in a hurry. How do I get a Hello World module working?
|
||||
Important things to notice:
|
||||
|
||||
* The {@link angular.Module Module} API
|
||||
* The reference to `myApp` module in `<html ng-app="myApp">`.
|
||||
* The reference to `myApp` module in `<div ng-app="myApp">`.
|
||||
This is what bootstraps the app using your module.
|
||||
* The empty array in `angular.module('myApp', [])`.
|
||||
This array is the list of modules `myApp` depends on.
|
||||
@@ -126,7 +128,7 @@ The above is a suggestion. Tailor it to your needs.
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should add Hello to the name', function() {
|
||||
expect(element(by.binding("{{ greeting }}")).getText()).toEqual('Bonjour World!');
|
||||
expect(element(by.binding("greeting")).getText()).toEqual('Bonjour World!');
|
||||
});
|
||||
</file>
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
@ngdoc overview
|
||||
@name Running in Production
|
||||
@description
|
||||
|
||||
# Running an AngularJS App in Production
|
||||
|
||||
There are a few things you might consider when running your AngularJS application in production.
|
||||
|
||||
|
||||
## Disabling Debug Data
|
||||
|
||||
By default AngularJS attaches information about scopes to DOM nodes, and adds CSS classes
|
||||
to data-bound elements. The information that is not included is:
|
||||
|
||||
As a result of `ngBind`, `ngBindHtml` or `{{...}}` interpolations, binding data and CSS class
|
||||
`ng-class` is attached to the corresponding element.
|
||||
|
||||
Where the compiler has created a new scope, the scope and either `ng-scope` or `ng-isolated-scope`
|
||||
CSS class are attached to the corresponding element. These scope references can then be accessed via
|
||||
`element.scope()` and `element.isolateScope()`.
|
||||
|
||||
Tools like [Protractor](github.com/angular/protractor) and
|
||||
[Batarang](https://github.com/angular/angularjs-batarang) need this information to run,
|
||||
but you can disable this in production for a significant performance boost with:
|
||||
|
||||
```js
|
||||
myApp.config(['$compileProvider', function ($compileProvider) {
|
||||
$compileProvider.debugInfoEnabled(false);
|
||||
}]);
|
||||
```
|
||||
|
||||
If you wish to debug an application with this information then you should open up a debug
|
||||
console in the browser then call this method directly in this console:
|
||||
|
||||
```js
|
||||
angular.reloadWithDebugInfo();
|
||||
```
|
||||
|
||||
The page should reload and the debug information should now be available.
|
||||
|
||||
For more see the docs pages on {@link ng.$compileProvider#debugInfoEnabled `$compileProvider`}
|
||||
and {@link ng/function/angular.reloadWithDebugInfo `angular.reloadWithDebugInfo`}.
|
||||
@@ -132,9 +132,11 @@ In the code above, we see how the `apiToken` service is defined via the Factory
|
||||
on the `clientId` service. The factory service then uses NSA-proof encryption to produce an authentication
|
||||
token.
|
||||
|
||||
Note: It is best practice to name the factory functions as `<serviceId>Factory`
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:** name the factory functions as `<serviceId>Factory`
|
||||
(e.g. apiTokenFactory). While this naming convention is not required, it helps when navigating the code base
|
||||
or looking at stack traces in the debugger.
|
||||
</div>
|
||||
|
||||
Just like with Value recipe, Factory recipe can create a service of any type, whether it be a
|
||||
primitive, object literal, function, or even an instance of a custom type.
|
||||
@@ -193,8 +195,7 @@ that would mess with the teachers.
|
||||
|
||||
## Provider Recipe
|
||||
|
||||
There are two more recipe types left to cover. They are both fairly specialized and are used
|
||||
infrequently. As already mentioned in the intro, the Provider recipe is the core recipe type and
|
||||
As already mentioned in the intro, the Provider recipe is the core recipe type and
|
||||
all the other recipe types are just syntactic sugar on top of it. It is the most verbose recipe
|
||||
with the most abilities, but for most services it's overkill.
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ may have several child scopes.
|
||||
The application can have multiple scopes, because some {@link guide/directive directives} create
|
||||
new child scopes (refer to directive documentation to see which directives create new scopes).
|
||||
When new scopes are created, they are added as children of their parent scope. This creates a tree
|
||||
structure which parallels the DOM where they're attached
|
||||
structure which parallels the DOM where they're attached.
|
||||
|
||||
When Angular evaluates `{{name}}`, it first looks at the scope associated with the given
|
||||
element for the `name` property. If no such property is found, it searches the parent scope
|
||||
|
||||
@@ -62,13 +62,13 @@ minified AngularJS files:
|
||||
|
||||
```shell
|
||||
# Clone your Github repository:
|
||||
git clone git@github.com:<github username>/angular.js.git
|
||||
git clone "git@github.com:<github username>/angular.js.git"
|
||||
|
||||
# Go to the AngularJS directory:
|
||||
cd angular.js
|
||||
|
||||
# Add the main AngularJS repository as an upstream remote to your repository:
|
||||
git remote add upstream https://github.com/angular/angular.js.git
|
||||
git remote add upstream "https://github.com/angular/angular.js.git"
|
||||
|
||||
# Install node.js dependencies:
|
||||
npm install
|
||||
@@ -126,13 +126,13 @@ made available a local web server based on Node.js.
|
||||
```
|
||||
|
||||
2. To access the local server, enter the following URL into your web browser:
|
||||
```
|
||||
```text
|
||||
http://localhost:8000/
|
||||
```
|
||||
By default, it serves the contents of the AngularJS project directory.
|
||||
|
||||
3. To access the locally served docs, visit this URL:
|
||||
```
|
||||
```text
|
||||
http://localhost:8000/build/docs/
|
||||
```
|
||||
|
||||
@@ -165,7 +165,7 @@ change. To execute tests in this mode run:
|
||||
2. To capture more browsers, open this URL in the desired browser (URL might be different if you have multiple instance
|
||||
of Karma running, read Karma's console output for the correct URL):
|
||||
|
||||
```shell
|
||||
```text
|
||||
http://localhost:9876/
|
||||
```
|
||||
|
||||
|
||||
@@ -83,4 +83,4 @@ after the core `angular.js` file:
|
||||
our docs, or even more importantly, view the docs offline.
|
||||
|
||||
* __`i18n`__ - this directory contains locale specific `ngLocale` angular modules to override the defaults
|
||||
defined in the `ng` module.
|
||||
defined in the `ng` module.
|
||||
|
||||
@@ -16,7 +16,7 @@ Because HTML has Angular brackets and "ng" sounds like "Angular".
|
||||
AngularJS fits the definition of a framework the best, even though it's much more lightweight than
|
||||
a typical framework and that's why many confuse it with a library.
|
||||
|
||||
AngularJS is 100% JavaScript, 100% client side and compatible with both desktop and mobile browsers.
|
||||
AngularJS is 100% JavaScript, 100% client-side and compatible with both desktop and mobile browsers.
|
||||
So it's definitely not a plugin or some other native browser extension.
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ Yes. See instructions in {@link downloading}.
|
||||
### What browsers does Angular work with?
|
||||
|
||||
We run our extensive test suite against the following browsers: Safari, Chrome, Firefox, Opera,
|
||||
IE8, IE9 and mobile browsers (Android, Chrome Mobile, iOS Safari). See {@link guide/ie Internet
|
||||
IE9 and mobile browsers (Android, Chrome Mobile, iOS Safari). See {@link guide/ie Internet
|
||||
Explorer Compatibility} for more details in supporting legacy IE browsers.
|
||||
|
||||
|
||||
@@ -75,14 +75,15 @@ The size of the file is < 36KB compressed and minified.
|
||||
Yes, you can use widgets from the [Closure Library](https://developers.google.com/closure/library/)
|
||||
in Angular.
|
||||
|
||||
|
||||
### Does Angular use the jQuery library?
|
||||
|
||||
Yes, Angular can use [jQuery](http://jquery.com/) if it's present in your app when the
|
||||
application is being bootstrapped. If jQuery is not present in your script path, Angular falls back
|
||||
to its own implementation of the subset of jQuery that we call {@link angular.element jQLite}.
|
||||
|
||||
Due to a change to use `on()`/`off()` rather than `bind()`/`unbind()`, Angular 1.2 only operates with
|
||||
jQuery 1.7.1 or above. However, Angular does not currently support jQuery 2.x or above.
|
||||
Angular 1.3 only supports jQuery 2.1 or above. jQuery 1.7 and newer might work correctly with Angular
|
||||
but we don't guarantee that.
|
||||
|
||||
|
||||
### What is testability like in Angular?
|
||||
@@ -114,7 +115,7 @@ make our schwag will be happy to do a custom run for you, based on our existing
|
||||
they'll waive the setup costs, and you can order any quantity you need.
|
||||
|
||||
**Stickers**
|
||||
For orders of 250 stickers or more within Canada or the United States, contact Tom Witting (or anyone in sales) via email at tom@stickergiant.com, and tell him you want to order some AngularJS
|
||||
For orders of 250 stickers or more within Canada or the United States, contact Tom Witting (or anyone in sales) via email at <tom@stickergiant.com>, and tell him you want to order some AngularJS
|
||||
stickers just like the ones in job #42711. You'll have to give them your own info for billing and shipping.
|
||||
|
||||
As long as the design stays exactly the same, [StickerGiant](http://www.stickergiant.com) will give you a reorder discount.
|
||||
|
||||
@@ -44,8 +44,8 @@ really digging into it. If you're looking for a shorter introduction to AngularJ
|
||||
|
||||
# Get Started
|
||||
|
||||
The rest of this page explains how you can set up your machine to work with the code on your local
|
||||
machine. If you just want to read the tutorial then you can just go straight to the first step:
|
||||
The rest of this page explains how you can set up your local machine for development.
|
||||
If you just want to read the tutorial then you can just go straight to the first step:
|
||||
[Step 0 - Bootstrapping](tutorial/step_00).
|
||||
|
||||
# Working with the code
|
||||
@@ -195,6 +195,11 @@ You can now browse to the application at:
|
||||
http://localhost:8000/app/index.html
|
||||
```
|
||||
|
||||
<div class="alert alert-info">
|
||||
To serve the web app on a different ip address or port, edit the "start" script within package.json.
|
||||
You can `-a` to set the address and `-p` to set the port.
|
||||
</div>
|
||||
|
||||
### Running Unit Tests
|
||||
|
||||
We use unit tests to ensure that the JavaScript code in our application is operating correctly.
|
||||
|
||||
@@ -31,7 +31,7 @@ npm install
|
||||
|
||||
To see the app running in a browser, open a *separate* terminal/command line tab or window, then
|
||||
run `npm start` to start the web server. Now, open a browser window for the app and navigate to
|
||||
<a href="http://localhost:8000/app/index.html" target="_blank">`http://localhost:8000/app/index.html`</a>
|
||||
<a href="http://localhost:8000/app/" target="_blank">`http://localhost:8000/app/`</a>
|
||||
|
||||
You can now see the page in your browser. It's not very exciting, but that's OK.
|
||||
|
||||
|
||||
@@ -59,12 +59,17 @@ tag as the template.
|
||||
by the value of the expressions.
|
||||
|
||||
We have added a new directive, called `ng-controller`, which attaches a `PhoneListCtrl`
|
||||
__controller__ to the DOM at this point:
|
||||
__controller__ to the <body> tag. At this point:
|
||||
|
||||
* The expressions in curly braces (`{{phone.name}}` and `{{phone.snippet}}` denote
|
||||
bindings, which are referring to our application model, which is set up in our `PhoneListCtrl`
|
||||
controller.
|
||||
|
||||
<div class="alert alert-info">
|
||||
Note: We have specified an {@link angular.Module Angular Module} to load using `ng-app="phonecatApp"`,
|
||||
where `phonecatApp` is the name of our module. This module will contain the `PhoneListCtrl`.
|
||||
</div>
|
||||
|
||||
<img class="diagram" src="img/tutorial/tutorial_02.png">
|
||||
|
||||
## Model and Controller
|
||||
@@ -205,6 +210,11 @@ To run the tests, and then watch the files for changes: `npm test`.
|
||||
* To rerun the tests, just change any of the source or test .js files. Karma will notice the change
|
||||
and will rerun the tests for you. Now isn't that sweet?
|
||||
|
||||
<div class="alert alert-info">
|
||||
Make sure you don't minimize the browser that Karma opened. On some OS, memory assigned to a minimized
|
||||
browser is limited, which results in your karma tests running extremely slow.
|
||||
</div>
|
||||
|
||||
# Experiments
|
||||
|
||||
* Add another binding to `index.html`. For example:
|
||||
@@ -255,4 +265,4 @@ to the app.
|
||||
|
||||
[jasmine]: http://jasmine.github.io/
|
||||
[jasmine-docs]: http://jasmine.github.io/1.3/introduction.html
|
||||
[karma]: http://karma-runner.github.io/
|
||||
[karma]: http://karma-runner.github.io/
|
||||
|
||||
@@ -128,7 +128,9 @@ will exit after the test run and will not automatically rerun the test suite on
|
||||
To rerun the test suite, execute `npm run protractor` again.
|
||||
|
||||
<div class="alert alert-info">
|
||||
Note: You must ensure you've installed the protractor and updated webdriver prior to running the
|
||||
Note: You must ensure your application is being served via a web-server to test with protractor.
|
||||
You can do this using `npm start`.
|
||||
You also need to ensure you've installed the protractor and updated webdriver prior to running the
|
||||
`npm run protractor`. You can do this by issuing `npm install` and `npm run update-webdriver` into
|
||||
your terminal.
|
||||
</div>
|
||||
@@ -143,16 +145,39 @@ Display the current value of the `query` model by adding a `{{query}}` binding i
|
||||
### Display Query in Title
|
||||
Let's see how we can get the current value of the `query` model to appear in the HTML page title.
|
||||
|
||||
* Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`:
|
||||
* Add an end-to-end test into the `describe` block, `test/e2e/scenarios.js` should look like this:
|
||||
|
||||
```js
|
||||
it('should display the current filter value in the title bar', function() {
|
||||
describe('PhoneCat App', function() {
|
||||
|
||||
expect(browser.getTitle()).toMatch(/Google Phone Gallery:\s*$/);
|
||||
|
||||
element(by.model('query')).sendKeys('nexus');
|
||||
|
||||
expect(browser.getTitle()).toMatch(/Google Phone Gallery: nexus$/);
|
||||
describe('Phone list view', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser.get('app/index.html');
|
||||
});
|
||||
|
||||
var phoneList = element.all(by.repeater('phone in phones'));
|
||||
var query = element(by.model('query'));
|
||||
|
||||
it('should filter the phone list as user types into the search box', function() {
|
||||
expect(phoneList.count()).toBe(3);
|
||||
|
||||
query.sendKeys('nexus');
|
||||
expect(phoneList.count()).toBe(1);
|
||||
|
||||
query.clear();
|
||||
query.sendKeys('motorola');
|
||||
expect(phoneList.count()).toBe(2);
|
||||
});
|
||||
|
||||
it('should display the current filter value in the title bar', function() {
|
||||
query.clear();
|
||||
expect(browser.getTitle()).toMatch(/Google Phone Gallery:\s*$/);
|
||||
|
||||
query.sendKeys('nexus');
|
||||
expect(browser.getTitle()).toMatch(/Google Phone Gallery: nexus$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ __`test/e2e/scenarios.js`__:
|
||||
it('should render phone specific links', function() {
|
||||
var query = element(by.model('query'));
|
||||
query.sendKeys('nexus');
|
||||
element(by.css('.phones li a')).click();
|
||||
element.all(by.css('.phones li a')).first().click();
|
||||
browser.getLocationAbsUrl().then(function(url) {
|
||||
expect(url.split('#')[1]).toBe('/phones/nexus-s');
|
||||
});
|
||||
|
||||
@@ -80,14 +80,14 @@ AngularJS, so it's important for you to understand a thing or two about how it w
|
||||
|
||||
When the application bootstraps, Angular creates an injector that will be used to find and inject all
|
||||
of the services that are required by your app. The injector itself doesn't know anything about what
|
||||
`$http` or `$route` services do, in fact it doesn't even know about the existence of these services
|
||||
unless it is configured with proper module definitions.
|
||||
`$http` or `$route` services do, in fact it doesn't even know about the existence of these services
|
||||
unless it is configured with proper module definitions.
|
||||
|
||||
The injector only carries out the following steps :
|
||||
The injector only carries out the following steps :
|
||||
|
||||
* load the module definition(s) that you specify in your app
|
||||
* register all Providers defined in these module definitions
|
||||
* when asked to do so, inject a specified function and any necessary dependencies (services) that
|
||||
* when asked to do so, inject a specified function and any necessary dependencies (services) that
|
||||
it lazily instantiates via their Providers.
|
||||
|
||||
Providers are objects that provide (create) instances of services and expose configuration APIs
|
||||
@@ -202,7 +202,7 @@ moved the controllers into their own module `phonecatControllers` (as shown belo
|
||||
|
||||
We added `angular-route.js` to `index.html` and created a new `phonecatControllers` module in
|
||||
`controllers.js`. That's not all we need to do to be able to use their code, however. We also have
|
||||
to add the modules dependencies of our app. By listing these two modules as dependencies of
|
||||
to add the modules as dependencies of our app. By listing these two modules as dependencies of
|
||||
`phonecatApp`, we can use the directives and services they provide.
|
||||
|
||||
|
||||
|
||||
@@ -102,6 +102,50 @@ __`test/e2e/scenarios.js`:__
|
||||
You can now rerun `npm run protractor` to see the tests run.
|
||||
|
||||
|
||||
You also have to refactor one of your unit tests because of the addition of the `mainImageUrl`
|
||||
model property to the `PhoneDetailCtrl` controller. Below, we create the function `xyzPhoneData`
|
||||
which returns the appropriate json with the `images` attribute in order to get the test to pass.
|
||||
|
||||
__`test/unit/controllersSpec.js`:__
|
||||
|
||||
```js
|
||||
...
|
||||
beforeEach(module('phonecatApp'));
|
||||
|
||||
...
|
||||
|
||||
describe('PhoneDetailCtrl', function(){
|
||||
var scope, $httpBackend, ctrl,
|
||||
xyzPhoneData = function() {
|
||||
return {
|
||||
name: 'phone xyz',
|
||||
images: ['image/url1.png', 'image/url2.png']
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
beforeEach(inject(function(_$httpBackend_, $rootScope, $routeParams, $controller) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpBackend.expectGET('phones/xyz.json').respond(xyzPhoneData());
|
||||
|
||||
$routeParams.phoneId = 'xyz';
|
||||
scope = $rootScope.$new();
|
||||
ctrl = $controller('PhoneDetailCtrl', {$scope: scope});
|
||||
}));
|
||||
|
||||
|
||||
it('should fetch phone detail', function() {
|
||||
expect(scope.phone).toBeUndefined();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(scope.phone).toEqual(xyzPhoneData());
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Your unit tests should now be passing.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
* Let's add a new controller method to `PhoneDetailCtrl`:
|
||||
|
||||
@@ -104,7 +104,8 @@ __`app/index.html`.__
|
||||
```
|
||||
|
||||
<div class="alert alert-error">
|
||||
**Important:** Be sure to use jQuery version `1.10.x`. AngularJS does not yet support jQuery `2.x`.
|
||||
**Important:** Be sure to use jQuery version 2.1 or newer when using Angular 1.3; jQuery 1.x is
|
||||
not officially supported.
|
||||
Be sure to load jQuery before all AngularJS scripts, otherwise AngularJS won't detect jQuery and
|
||||
animations will not work as expected.
|
||||
</div>
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ gulp.task('assets', ['bower'], function() {
|
||||
copyComponent('open-sans-fontface'),
|
||||
copyComponent('lunr.js','/*.js'),
|
||||
copyComponent('google-code-prettify'),
|
||||
copyComponent('jquery', '/jquery.*'),
|
||||
copyComponent('jquery', '/dist/*.js'),
|
||||
copyComponent('marked', '/**/*.js', '../node_modules', 'package.json')
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2,17 +2,25 @@
|
||||
'use strict';
|
||||
|
||||
var isFunction = function isFunction(value){return typeof value == 'function';};
|
||||
var isPromiseLike = function isPromiseLike(obj) {return obj && isFunction(obj.then);};
|
||||
var isObject = function isObject(value){return value != null && typeof value === 'object';};
|
||||
var minErr = function minErr (module, constructor) {
|
||||
return function (){
|
||||
var ErrorConstructor = constructor || Error;
|
||||
throw new ErrorConstructor(module + arguments[0] + arguments[1]);
|
||||
};
|
||||
};
|
||||
|
||||
var $q = qFactory(process.nextTick, function noopExceptionHandler() {});
|
||||
|
||||
exports.fulfilled = $q.resolve;
|
||||
exports.resolved = $q.resolve;
|
||||
exports.rejected = $q.reject;
|
||||
exports.pending = function () {
|
||||
exports.deferred = function () {
|
||||
var deferred = $q.defer();
|
||||
|
||||
return {
|
||||
promise: deferred.promise,
|
||||
fulfill: deferred.resolve,
|
||||
resolve: deferred.resolve,
|
||||
reject: deferred.reject
|
||||
};
|
||||
};
|
||||
|
||||
@@ -12,9 +12,9 @@ set -e
|
||||
# before_script:
|
||||
# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash
|
||||
|
||||
CONNECT_URL="https://d2nkw87yt5k0to.cloudfront.net/downloads/sc-latest-linux.tar.gz"
|
||||
CONNECT_URL="https://d2nkw87yt5k0to.cloudfront.net/downloads/sc-4.3-linux.tar.gz"
|
||||
CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
|
||||
CONNECT_DOWNLOAD="sc-latest-linux.tar.gz"
|
||||
CONNECT_DOWNLOAD="sc-4.3-linux.tar.gz"
|
||||
|
||||
CONNECT_LOG="$LOGS_DIR/sauce-connect"
|
||||
CONNECT_STDOUT="$LOGS_DIR/sauce-connect.stdout"
|
||||
|
||||
Generated
+501
-6
@@ -1,5 +1,497 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"angular-benchpress": {
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"bootstrap": {
|
||||
"version": "3.2.0"
|
||||
},
|
||||
"grunt": {
|
||||
"version": "0.4.5",
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "0.1.22"
|
||||
},
|
||||
"coffee-script": {
|
||||
"version": "1.3.3"
|
||||
},
|
||||
"colors": {
|
||||
"version": "0.6.2"
|
||||
},
|
||||
"dateformat": {
|
||||
"version": "1.0.2-1.2.3"
|
||||
},
|
||||
"eventemitter2": {
|
||||
"version": "0.4.14"
|
||||
},
|
||||
"findup-sync": {
|
||||
"version": "0.1.3",
|
||||
"dependencies": {
|
||||
"glob": {
|
||||
"version": "3.2.11",
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.1"
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "0.3.0",
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "2.5.0"
|
||||
},
|
||||
"sigmund": {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "2.4.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "3.1.21",
|
||||
"dependencies": {
|
||||
"graceful-fs": {
|
||||
"version": "1.2.3"
|
||||
},
|
||||
"inherits": {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"hooker": {
|
||||
"version": "0.2.3"
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.2.11"
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "0.2.14",
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "2.5.0"
|
||||
},
|
||||
"sigmund": {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nopt": {
|
||||
"version": "1.0.10",
|
||||
"dependencies": {
|
||||
"abbrev": {
|
||||
"version": "1.0.5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "0.9.2"
|
||||
},
|
||||
"underscore.string": {
|
||||
"version": "2.2.1"
|
||||
},
|
||||
"which": {
|
||||
"version": "1.0.5"
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "2.0.5",
|
||||
"dependencies": {
|
||||
"argparse": {
|
||||
"version": "0.1.15",
|
||||
"dependencies": {
|
||||
"underscore": {
|
||||
"version": "1.4.4"
|
||||
},
|
||||
"underscore.string": {
|
||||
"version": "2.3.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"esprima": {
|
||||
"version": "1.0.4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"exit": {
|
||||
"version": "0.1.2"
|
||||
},
|
||||
"getobject": {
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"grunt-legacy-util": {
|
||||
"version": "0.2.0"
|
||||
},
|
||||
"grunt-legacy-log": {
|
||||
"version": "0.1.1",
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "2.4.1"
|
||||
},
|
||||
"underscore.string": {
|
||||
"version": "2.3.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"http-server": {
|
||||
"version": "0.6.1",
|
||||
"dependencies": {
|
||||
"colors": {
|
||||
"version": "0.6.2"
|
||||
},
|
||||
"optimist": {
|
||||
"version": "0.5.2",
|
||||
"dependencies": {
|
||||
"wordwrap": {
|
||||
"version": "0.0.2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"union": {
|
||||
"version": "0.3.8",
|
||||
"dependencies": {
|
||||
"pkginfo": {
|
||||
"version": "0.2.3"
|
||||
},
|
||||
"qs": {
|
||||
"version": "0.5.6"
|
||||
}
|
||||
}
|
||||
},
|
||||
"ecstatic": {
|
||||
"version": "0.4.13",
|
||||
"dependencies": {
|
||||
"mime": {
|
||||
"version": "1.2.11"
|
||||
},
|
||||
"ent": {
|
||||
"version": "0.0.7"
|
||||
},
|
||||
"optimist": {
|
||||
"version": "0.3.7",
|
||||
"dependencies": {
|
||||
"wordwrap": {
|
||||
"version": "0.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"portfinder": {
|
||||
"version": "0.2.1",
|
||||
"dependencies": {
|
||||
"mkdirp": {
|
||||
"version": "0.0.7"
|
||||
}
|
||||
}
|
||||
},
|
||||
"opener": {
|
||||
"version": "1.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"karma": {
|
||||
"version": "0.12.22",
|
||||
"dependencies": {
|
||||
"di": {
|
||||
"version": "0.0.1"
|
||||
},
|
||||
"socket.io": {
|
||||
"version": "0.9.17",
|
||||
"dependencies": {
|
||||
"socket.io-client": {
|
||||
"version": "0.9.16",
|
||||
"dependencies": {
|
||||
"uglify-js": {
|
||||
"version": "1.2.5"
|
||||
},
|
||||
"ws": {
|
||||
"version": "0.4.32",
|
||||
"dependencies": {
|
||||
"commander": {
|
||||
"version": "2.1.0"
|
||||
},
|
||||
"nan": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"tinycolor": {
|
||||
"version": "0.0.1"
|
||||
},
|
||||
"options": {
|
||||
"version": "0.0.5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"xmlhttprequest": {
|
||||
"version": "1.4.2"
|
||||
},
|
||||
"active-x-obfuscator": {
|
||||
"version": "0.0.1",
|
||||
"dependencies": {
|
||||
"zeparser": {
|
||||
"version": "0.0.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"policyfile": {
|
||||
"version": "0.0.4"
|
||||
},
|
||||
"base64id": {
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"redis": {
|
||||
"version": "0.7.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"chokidar": {
|
||||
"version": "0.8.4",
|
||||
"dependencies": {
|
||||
"fsevents": {
|
||||
"version": "0.2.1",
|
||||
"dependencies": {
|
||||
"nan": {
|
||||
"version": "0.8.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"recursive-readdir": {
|
||||
"version": "0.0.2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "3.2.11",
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.1"
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "0.3.0",
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "2.5.0"
|
||||
},
|
||||
"sigmund": {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "0.2.14",
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "2.5.0"
|
||||
},
|
||||
"sigmund": {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"http-proxy": {
|
||||
"version": "0.10.4",
|
||||
"dependencies": {
|
||||
"pkginfo": {
|
||||
"version": "0.3.0"
|
||||
},
|
||||
"utile": {
|
||||
"version": "0.2.1",
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "0.2.10"
|
||||
},
|
||||
"deep-equal": {
|
||||
"version": "0.2.1"
|
||||
},
|
||||
"i": {
|
||||
"version": "0.3.2"
|
||||
},
|
||||
"ncp": {
|
||||
"version": "0.4.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"optimist": {
|
||||
"version": "0.6.1",
|
||||
"dependencies": {
|
||||
"wordwrap": {
|
||||
"version": "0.0.2"
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.10"
|
||||
}
|
||||
}
|
||||
},
|
||||
"q": {
|
||||
"version": "0.9.7"
|
||||
},
|
||||
"colors": {
|
||||
"version": "0.6.2"
|
||||
},
|
||||
"lodash": {
|
||||
"version": "2.4.1"
|
||||
},
|
||||
"mime": {
|
||||
"version": "1.2.11"
|
||||
},
|
||||
"log4js": {
|
||||
"version": "0.6.18",
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "0.1.15"
|
||||
},
|
||||
"semver": {
|
||||
"version": "1.1.4"
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "1.0.31",
|
||||
"dependencies": {
|
||||
"core-util-is": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"isarray": {
|
||||
"version": "0.0.1"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.25-1"
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"useragent": {
|
||||
"version": "2.0.9",
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "2.2.4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "2.0.3"
|
||||
},
|
||||
"connect": {
|
||||
"version": "2.12.0",
|
||||
"dependencies": {
|
||||
"batch": {
|
||||
"version": "0.5.0"
|
||||
},
|
||||
"qs": {
|
||||
"version": "0.6.6"
|
||||
},
|
||||
"cookie-signature": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"buffer-crc32": {
|
||||
"version": "0.2.1"
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"send": {
|
||||
"version": "0.1.4",
|
||||
"dependencies": {
|
||||
"range-parser": {
|
||||
"version": "0.0.4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bytes": {
|
||||
"version": "0.2.1"
|
||||
},
|
||||
"fresh": {
|
||||
"version": "0.2.0"
|
||||
},
|
||||
"pause": {
|
||||
"version": "0.0.1"
|
||||
},
|
||||
"uid2": {
|
||||
"version": "0.0.3"
|
||||
},
|
||||
"debug": {
|
||||
"version": "0.8.1"
|
||||
},
|
||||
"methods": {
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"raw-body": {
|
||||
"version": "1.1.2"
|
||||
},
|
||||
"negotiator": {
|
||||
"version": "0.3.0"
|
||||
},
|
||||
"multiparty": {
|
||||
"version": "2.2.0",
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "1.1.13",
|
||||
"dependencies": {
|
||||
"core-util-is": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"isarray": {
|
||||
"version": "0.0.1"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.25-1"
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"stream-counter": {
|
||||
"version": "0.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.1.38",
|
||||
"dependencies": {
|
||||
"amdefine": {
|
||||
"version": "0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"karma-chrome-launcher": {
|
||||
"version": "0.1.4"
|
||||
},
|
||||
"minimist": {
|
||||
"version": "1.1.0"
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.0",
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "0.0.8"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.2.8"
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.6.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bower": {
|
||||
"version": "1.2.8",
|
||||
"dependencies": {
|
||||
@@ -502,7 +994,7 @@
|
||||
}
|
||||
},
|
||||
"dgeni-packages": {
|
||||
"version": "0.9.5",
|
||||
"version": "0.9.7",
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "2.4.1"
|
||||
@@ -2664,7 +3156,7 @@
|
||||
"version": "0.0.2"
|
||||
},
|
||||
"promises-aplus-tests": {
|
||||
"version": "1.3.2",
|
||||
"version": "2.0.4",
|
||||
"dependencies": {
|
||||
"mocha": {
|
||||
"version": "1.11.0",
|
||||
@@ -2738,7 +3230,7 @@
|
||||
}
|
||||
},
|
||||
"protractor": {
|
||||
"version": "1.0.0-rc5",
|
||||
"version": "1.2.0-beta1",
|
||||
"dependencies": {
|
||||
"request": {
|
||||
"version": "2.36.0",
|
||||
@@ -2762,7 +3254,7 @@
|
||||
"version": "0.12.1",
|
||||
"dependencies": {
|
||||
"punycode": {
|
||||
"version": "1.2.4"
|
||||
"version": "1.3.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2831,7 +3323,7 @@
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"jasminewd": {
|
||||
"version": "1.0.3"
|
||||
"version": "1.0.4"
|
||||
},
|
||||
"saucelabs": {
|
||||
"version": "0.1.1"
|
||||
@@ -2869,11 +3361,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"q": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"lodash": {
|
||||
"version": "2.4.1"
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.2.6",
|
||||
"version": "0.2.7",
|
||||
"dependencies": {
|
||||
"source-map": {
|
||||
"version": "0.1.32",
|
||||
|
||||
+5
-4
@@ -33,10 +33,10 @@
|
||||
"karma-sauce-launcher": "0.2.0",
|
||||
"karma-script-launcher": "0.1.0",
|
||||
"karma-browserstack-launcher": "0.0.7",
|
||||
"protractor": "1.0.0-rc5",
|
||||
"protractor": "1.2.0-beta1",
|
||||
"yaml-js": "~0.0.8",
|
||||
"rewire": "1.1.3",
|
||||
"promises-aplus-tests": "~1.3.2",
|
||||
"promises-aplus-tests": "~2.0.4",
|
||||
"semver": "~2.1.0",
|
||||
"lodash": "~2.1.0",
|
||||
"browserstacktunnel-wrapper": "~1.1.1",
|
||||
@@ -48,12 +48,13 @@
|
||||
"canonical-path": "0.0.2",
|
||||
"winston": "~0.7.2",
|
||||
"dgeni": "^0.3.0",
|
||||
"dgeni-packages": "^0.9.5",
|
||||
"dgeni-packages": "^0.9.7",
|
||||
"gulp-jshint": "~1.4.2",
|
||||
"jshint-stylish": "~0.1.5",
|
||||
"node-html-encoder": "0.0.2",
|
||||
"sorted-object": "^1.0.0",
|
||||
"qq": "^0.3.5"
|
||||
"qq": "^0.3.5",
|
||||
"angular-benchpress": "0.x.x"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ var config = require('./protractor-shared-conf').config;
|
||||
|
||||
config.specs = [
|
||||
'build/docs/ptore2e/**/*.js',
|
||||
'test/e2e/docsAppE2E.js'
|
||||
'docs/app/e2e/docsAppE2E.js'
|
||||
];
|
||||
|
||||
config.capabilities = {
|
||||
|
||||
@@ -5,7 +5,7 @@ exports.config = {
|
||||
|
||||
specs: [
|
||||
'build/docs/ptore2e/**/*.js',
|
||||
'test/e2e/docsAppE2E.js'
|
||||
'docs/app/e2e/docsAppE2E.js'
|
||||
],
|
||||
|
||||
capabilities: {
|
||||
|
||||
@@ -23,9 +23,6 @@ function cleanModule(module, name) {
|
||||
if (name === 'chokidar') {
|
||||
if (module.version === '0.8.1') {
|
||||
delete module.dependencies;
|
||||
} else if ( module.version !== '0.8.2') {
|
||||
throw new Error("Unfamiliar chokidar version (v" + module.version +
|
||||
") , please check status of https://github.com/paulmillr/chokidar/pull/106");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ elif [ $JOB = "e2e" ]; then
|
||||
if [ $TEST_TARGET = "jquery" ]; then
|
||||
TARGET_SPECS="build/docs/ptore2e/**/*jquery_test.js"
|
||||
elif [ $TEST_TARGET = "doce2e" ]; then
|
||||
TARGET_SPECS="test/e2e/docsAppE2E.js"
|
||||
TARGET_SPECS="docs/app/e2e/docsAppE2E.js"
|
||||
fi
|
||||
grunt test:travis-protractor --specs "$TARGET_SPECS"
|
||||
else
|
||||
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# normalize the working dir to the directory of the script
|
||||
cd $(dirname $0);
|
||||
|
||||
cd ../..
|
||||
curl "http://23.251.148.50:8000/tar/$TRAVIS_REPO_SLUG/$TRAVIS_COMMIT" | tar xz || true
|
||||
+7
-1
@@ -51,6 +51,7 @@
|
||||
"isFile": false,
|
||||
"isBlob": false,
|
||||
"isBoolean": false,
|
||||
"isPromiseLike": false,
|
||||
"trim": false,
|
||||
"isElement": false,
|
||||
"makeMap": false,
|
||||
@@ -78,14 +79,19 @@
|
||||
"encodeUriQuery": false,
|
||||
"angularInit": false,
|
||||
"bootstrap": false,
|
||||
"getTestability": false,
|
||||
"snake_case": false,
|
||||
"bindJQuery": false,
|
||||
"assertArg": false,
|
||||
"assertArgFn": false,
|
||||
"assertNotHasOwnProperty": false,
|
||||
"getter": false,
|
||||
"getBlockElements": false,
|
||||
"getBlockNodes": false,
|
||||
"createMap": false,
|
||||
"VALIDITY_STATE_PROPERTY": false,
|
||||
"reloadWithDebugInfo": false,
|
||||
|
||||
"skipDestroyOnNextJQueryCleanData": true,
|
||||
|
||||
/* filters.js */
|
||||
"getFirstThursdayOfYear": false,
|
||||
|
||||
+136
-77
@@ -10,7 +10,6 @@
|
||||
toString: true,
|
||||
ngMinErr: true,
|
||||
angularModule: true,
|
||||
nodeName_: true,
|
||||
uid: true,
|
||||
REGEX_STRING_REGEXP: true,
|
||||
VALIDITY_STATE_PROPERTY: true,
|
||||
@@ -47,6 +46,7 @@
|
||||
isFile: true,
|
||||
isBlob: true,
|
||||
isBoolean: true,
|
||||
isPromiseLike: true,
|
||||
trim: true,
|
||||
isElement: true,
|
||||
makeMap: true,
|
||||
@@ -74,14 +74,16 @@
|
||||
encodeUriQuery: true,
|
||||
angularInit: true,
|
||||
bootstrap: true,
|
||||
getTestability: true,
|
||||
snake_case: true,
|
||||
bindJQuery: true,
|
||||
assertArg: true,
|
||||
assertArgFn: true,
|
||||
assertNotHasOwnProperty: true,
|
||||
getter: true,
|
||||
getBlockElements: true,
|
||||
getBlockNodes: true,
|
||||
hasOwnProperty: true,
|
||||
createMap: true,
|
||||
*/
|
||||
|
||||
////////////////////////////////////
|
||||
@@ -168,7 +170,6 @@ var /** holds major version number for IE or NaN for real browsers */
|
||||
/** @name angular */
|
||||
angular = window.angular || (window.angular = {}),
|
||||
angularModule,
|
||||
nodeName_,
|
||||
uid = 0;
|
||||
|
||||
/**
|
||||
@@ -244,8 +245,11 @@ function forEach(obj, iterator, context) {
|
||||
}
|
||||
}
|
||||
} else if (isArray(obj) || isArrayLike(obj)) {
|
||||
var isPrimitive = typeof obj !== 'object';
|
||||
for (key = 0, length = obj.length; key < length; key++) {
|
||||
iterator.call(context, obj[key], key);
|
||||
if (isPrimitive || key in obj) {
|
||||
iterator.call(context, obj[key], key);
|
||||
}
|
||||
}
|
||||
} else if (obj.forEach && obj.forEach !== forEach) {
|
||||
obj.forEach(iterator, context);
|
||||
@@ -439,7 +443,10 @@ function isDefined(value){return typeof value !== 'undefined';}
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is an `Object` but not `null`.
|
||||
*/
|
||||
function isObject(value){return value != null && typeof value === 'object';}
|
||||
function isObject(value){
|
||||
// http://jsperf.com/isobject4
|
||||
return value !== null && typeof value === 'object';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -501,14 +508,7 @@ function isDate(value) {
|
||||
* @param {*} value Reference to check.
|
||||
* @returns {boolean} True if `value` is an `Array`.
|
||||
*/
|
||||
var isArray = (function() {
|
||||
if (!isFunction(Array.isArray)) {
|
||||
return function(value) {
|
||||
return toString.call(value) === '[object Array]';
|
||||
};
|
||||
}
|
||||
return Array.isArray;
|
||||
})();
|
||||
var isArray = Array.isArray;
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
@@ -569,19 +569,14 @@ function isBoolean(value) {
|
||||
}
|
||||
|
||||
|
||||
var trim = (function() {
|
||||
// native trim is way faster: http://jsperf.com/angular-trim-test
|
||||
// but IE doesn't have it... :-(
|
||||
// TODO: we should move this into IE/ES5 polyfill
|
||||
if (!String.prototype.trim) {
|
||||
return function(value) {
|
||||
return isString(value) ? value.replace(/^\s\s*/, '').replace(/\s\s*$/, '') : value;
|
||||
};
|
||||
}
|
||||
return function(value) {
|
||||
return isString(value) ? value.trim() : value;
|
||||
};
|
||||
})();
|
||||
function isPromiseLike(obj) {
|
||||
return obj && isFunction(obj.then);
|
||||
}
|
||||
|
||||
|
||||
var trim = function(value) {
|
||||
return isString(value) ? value.trim() : value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@@ -614,18 +609,8 @@ function makeMap(str) {
|
||||
}
|
||||
|
||||
|
||||
if (msie < 9) {
|
||||
nodeName_ = function(element) {
|
||||
element = element.nodeName ? element : element[0];
|
||||
return lowercase(
|
||||
(element.scopeName && element.scopeName != 'HTML')
|
||||
? element.scopeName + ':' + element.nodeName : element.nodeName
|
||||
);
|
||||
};
|
||||
} else {
|
||||
nodeName_ = function(element) {
|
||||
return lowercase(element.nodeName ? element.nodeName : element[0].nodeName);
|
||||
};
|
||||
function nodeName_(element) {
|
||||
return lowercase(element.nodeName || element[0].nodeName);
|
||||
}
|
||||
|
||||
|
||||
@@ -769,7 +754,8 @@ function copy(source, destination, stackSource, stackDest) {
|
||||
} else if (isDate(source)) {
|
||||
destination = new Date(source.getTime());
|
||||
} else if (isRegExp(source)) {
|
||||
destination = new RegExp(source.source);
|
||||
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
|
||||
destination.lastIndex = source.lastIndex;
|
||||
} else if (isObject(source)) {
|
||||
var emptyObject = Object.create(Object.getPrototypeOf(source));
|
||||
destination = copy(source, emptyObject, stackSource, stackDest);
|
||||
@@ -803,9 +789,13 @@ function copy(source, destination, stackSource, stackDest) {
|
||||
}
|
||||
} else {
|
||||
var h = destination.$$hashKey;
|
||||
forEach(destination, function(value, key) {
|
||||
delete destination[key];
|
||||
});
|
||||
if (isArray(destination)) {
|
||||
destination.length = 0;
|
||||
} else {
|
||||
forEach(destination, function(value, key) {
|
||||
delete destination[key];
|
||||
});
|
||||
}
|
||||
for ( var key in source) {
|
||||
if(source.hasOwnProperty(key)) {
|
||||
result = copy(source[key], null, stackSource, stackDest);
|
||||
@@ -824,24 +814,21 @@ function copy(source, destination, stackSource, stackDest) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a shallow copy of an object, an array or a primitive
|
||||
* Creates a shallow copy of an object, an array or a primitive.
|
||||
*
|
||||
* Assumes that there are no proto properties for objects.
|
||||
*/
|
||||
function shallowCopy(src, dst) {
|
||||
var i = 0;
|
||||
if (isArray(src)) {
|
||||
dst = dst || [];
|
||||
|
||||
for (; i < src.length; i++) {
|
||||
for (var i = 0, ii = src.length; i < ii; i++) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
} else if (isObject(src)) {
|
||||
dst = dst || {};
|
||||
|
||||
var keys = Object.keys(src);
|
||||
|
||||
for (var l = keys.length; i < l; i++) {
|
||||
var key = keys[i];
|
||||
|
||||
for (var key in src) {
|
||||
if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
@@ -897,7 +884,8 @@ function equals(o1, o2) {
|
||||
return true;
|
||||
}
|
||||
} else if (isDate(o1)) {
|
||||
return isDate(o2) && o1.getTime() == o2.getTime();
|
||||
if (!isDate(o2)) return false;
|
||||
return equals(o1.getTime(), o2.getTime());
|
||||
} else if (isRegExp(o1) && isRegExp(o2)) {
|
||||
return o1.toString() == o2.toString();
|
||||
} else {
|
||||
@@ -1169,13 +1157,14 @@ function encodeUriQuery(val, pctEncodeSpaces) {
|
||||
replace(/%3A/gi, ':').
|
||||
replace(/%24/g, '$').
|
||||
replace(/%2C/gi, ',').
|
||||
replace(/%3B/gi, ';').
|
||||
replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
|
||||
}
|
||||
|
||||
var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
|
||||
|
||||
function getNgAttribute(element, ngAttr) {
|
||||
var attr, i, ii = ngAttrPrefixes.length, j, jj;
|
||||
var attr, i, ii = ngAttrPrefixes.length;
|
||||
element = jqLite(element);
|
||||
for (i=0; i<ii; ++i) {
|
||||
attr = ngAttrPrefixes[i] + ngAttr;
|
||||
@@ -1403,17 +1392,29 @@ function bootstrap(element, modules, config) {
|
||||
|
||||
if (element.injector()) {
|
||||
var tag = (element[0] === document) ? 'document' : startingTag(element);
|
||||
throw ngMinErr('btstrpd', "App Already Bootstrapped with this Element '{0}'", tag);
|
||||
//Encode angle brackets to prevent input from being sanitized to empty string #8683
|
||||
throw ngMinErr(
|
||||
'btstrpd',
|
||||
"App Already Bootstrapped with this Element '{0}'",
|
||||
tag.replace(/</,'<').replace(/>/,'>'));
|
||||
}
|
||||
|
||||
modules = modules || [];
|
||||
modules.unshift(['$provide', function($provide) {
|
||||
$provide.value('$rootElement', element);
|
||||
}]);
|
||||
|
||||
if (config.debugInfoEnabled) {
|
||||
// Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
|
||||
modules.push(['$compileProvider', function($compileProvider) {
|
||||
$compileProvider.debugInfoEnabled(true);
|
||||
}]);
|
||||
}
|
||||
|
||||
modules.unshift('ng');
|
||||
var injector = createInjector(modules, config.strictDi);
|
||||
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
|
||||
function(scope, element, compile, injector) {
|
||||
function bootstrapApply(scope, element, compile, injector) {
|
||||
scope.$apply(function() {
|
||||
element.data('$injector', injector);
|
||||
compile(element)(scope);
|
||||
@@ -1423,8 +1424,14 @@ function bootstrap(element, modules, config) {
|
||||
return injector;
|
||||
};
|
||||
|
||||
var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
|
||||
var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
|
||||
|
||||
if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
|
||||
config.debugInfoEnabled = true;
|
||||
window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
|
||||
}
|
||||
|
||||
if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
|
||||
return doBootstrap();
|
||||
}
|
||||
@@ -1438,6 +1445,33 @@ function bootstrap(element, modules, config) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.reloadWithDebugInfo
|
||||
* @module ng
|
||||
* @description
|
||||
* Use this function to reload the current application with debug information turned on.
|
||||
* This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
|
||||
*
|
||||
* See {@link ng.$compileProvider#debugInfoEnabled} for more.
|
||||
*/
|
||||
function reloadWithDebugInfo() {
|
||||
window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
/*
|
||||
* @name angular.getTestability
|
||||
* @module ng
|
||||
* @description
|
||||
* Get the testability service for the instance of Angular on the given
|
||||
* element.
|
||||
* @param {DOMElement} element DOM element which is the root of angular application.
|
||||
*/
|
||||
function getTestability(rootElement) {
|
||||
return angular.element(rootElement).injector().get('$$testability');
|
||||
}
|
||||
|
||||
var SNAKE_CASE_REGEXP = /[A-Z]/g;
|
||||
function snake_case(name, separator) {
|
||||
separator = separator || '_';
|
||||
@@ -1446,12 +1480,21 @@ function snake_case(name, separator) {
|
||||
});
|
||||
}
|
||||
|
||||
var bindJQueryFired = false;
|
||||
var skipDestroyOnNextJQueryCleanData;
|
||||
function bindJQuery() {
|
||||
var originalCleanData;
|
||||
|
||||
if (bindJQueryFired) {
|
||||
return;
|
||||
}
|
||||
|
||||
// bind to jQuery if present;
|
||||
jQuery = window.jQuery;
|
||||
// Use jQuery if it exists with proper functionality, otherwise default to us.
|
||||
// Angular 1.2+ requires jQuery 1.7.1+ for on()/off() support.
|
||||
// Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
|
||||
// Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
|
||||
// versions. It will not work for sure with jQuery <1.7, though.
|
||||
if (jQuery && jQuery.fn.on) {
|
||||
jqLite = jQuery;
|
||||
extend(jQuery.fn, {
|
||||
@@ -1462,25 +1505,28 @@ function bindJQuery() {
|
||||
inheritedData: JQLitePrototype.inheritedData
|
||||
});
|
||||
|
||||
originalCleanData = jQuery.cleanData;
|
||||
// Prevent double-proxying.
|
||||
originalCleanData = originalCleanData.$$original || originalCleanData;
|
||||
|
||||
// All nodes removed from the DOM via various jQuery APIs like .remove()
|
||||
// are passed through jQuery.cleanData. Monkey-patch this method to fire
|
||||
// the $destroy event on all removed nodes.
|
||||
originalCleanData = jQuery.cleanData;
|
||||
jQuery.cleanData = function(elems) {
|
||||
for (var i = 0, elem; (elem = elems[i]) != null; i++) {
|
||||
jQuery(elem).triggerHandler('$destroy');
|
||||
if (!skipDestroyOnNextJQueryCleanData) {
|
||||
for (var i = 0, elem; (elem = elems[i]) != null; i++) {
|
||||
jQuery(elem).triggerHandler('$destroy');
|
||||
}
|
||||
} else {
|
||||
skipDestroyOnNextJQueryCleanData = false;
|
||||
}
|
||||
originalCleanData(elems);
|
||||
};
|
||||
jQuery.cleanData.$$original = originalCleanData;
|
||||
} else {
|
||||
jqLite = JQLite;
|
||||
}
|
||||
|
||||
angular.element = jqLite;
|
||||
|
||||
// Prevent double-proxying.
|
||||
bindJQueryFired = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1544,23 +1590,36 @@ function getter(obj, path, bindFnToScope) {
|
||||
/**
|
||||
* Return the DOM siblings between the first and last node in the given array.
|
||||
* @param {Array} array like object
|
||||
* @returns {DOMElement} object containing the elements
|
||||
* @returns {jqLite} jqLite collection containing the nodes
|
||||
*/
|
||||
function getBlockElements(nodes) {
|
||||
var startNode = nodes[0],
|
||||
endNode = nodes[nodes.length - 1];
|
||||
if (startNode === endNode) {
|
||||
return jqLite(startNode);
|
||||
}
|
||||
|
||||
var element = startNode;
|
||||
var elements = [element];
|
||||
function getBlockNodes(nodes) {
|
||||
// TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
|
||||
// collection, otherwise update the original collection.
|
||||
var node = nodes[0];
|
||||
var endNode = nodes[nodes.length - 1];
|
||||
var blockNodes = [node];
|
||||
|
||||
do {
|
||||
element = element.nextSibling;
|
||||
if (!element) break;
|
||||
elements.push(element);
|
||||
} while (element !== endNode);
|
||||
node = node.nextSibling;
|
||||
if (!node) break;
|
||||
blockNodes.push(node);
|
||||
} while (node !== endNode);
|
||||
|
||||
return jqLite(elements);
|
||||
return jqLite(blockNodes);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new object without a prototype. This object is useful for lookup without having to
|
||||
* guard against prototypically inherited properties via hasOwnProperty.
|
||||
*
|
||||
* Related micro-benchmarks:
|
||||
* - http://jsperf.com/object-create2
|
||||
* - http://jsperf.com/proto-map-lookup/2
|
||||
* - http://jsperf.com/for-in-vs-object-keys2
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
function createMap() {
|
||||
return Object.create(null);
|
||||
}
|
||||
|
||||
+10
-4
@@ -78,6 +78,8 @@
|
||||
$SceDelegateProvider,
|
||||
$SnifferProvider,
|
||||
$TemplateCacheProvider,
|
||||
$TemplateRequestProvider,
|
||||
$$TestabilityProvider,
|
||||
$TimeoutProvider,
|
||||
$$RAFProvider,
|
||||
$$AsyncCallbackProvider,
|
||||
@@ -117,11 +119,11 @@ function publishExternalAPI(angular){
|
||||
'element': jqLite,
|
||||
'forEach': forEach,
|
||||
'injector': createInjector,
|
||||
'noop':noop,
|
||||
'bind':bind,
|
||||
'noop': noop,
|
||||
'bind': bind,
|
||||
'toJson': toJson,
|
||||
'fromJson': fromJson,
|
||||
'identity':identity,
|
||||
'identity': identity,
|
||||
'isUndefined': isUndefined,
|
||||
'isDefined': isDefined,
|
||||
'isString': isString,
|
||||
@@ -135,8 +137,10 @@ function publishExternalAPI(angular){
|
||||
'lowercase': lowercase,
|
||||
'uppercase': uppercase,
|
||||
'callbacks': {counter: 0},
|
||||
'getTestability': getTestability,
|
||||
'$$minErr': minErr,
|
||||
'$$csp': csp
|
||||
'$$csp': csp,
|
||||
'reloadWithDebugInfo': reloadWithDebugInfo
|
||||
});
|
||||
|
||||
angularModule = setupModuleLoader(window);
|
||||
@@ -227,6 +231,8 @@ function publishExternalAPI(angular){
|
||||
$sceDelegate: $SceDelegateProvider,
|
||||
$sniffer: $SnifferProvider,
|
||||
$templateCache: $TemplateCacheProvider,
|
||||
$templateRequest: $TemplateRequestProvider,
|
||||
$$testability: $$TestabilityProvider,
|
||||
$timeout: $TimeoutProvider,
|
||||
$window: $WindowProvider,
|
||||
$$rAF: $$RAFProvider,
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
//try to bind to jquery now so that one can write angular.element().read()
|
||||
//try to bind to jquery now so that one can write jqLite(document).ready()
|
||||
//but we will rebind on bootstrap again.
|
||||
bindJQuery();
|
||||
|
||||
|
||||
+12
-10
@@ -14,21 +14,23 @@
|
||||
* The resulting string key is in 'type:hashKey' format.
|
||||
*/
|
||||
function hashKey(obj, nextUidFn) {
|
||||
var objType = typeof obj,
|
||||
key;
|
||||
var key = obj && obj.$$hashKey;
|
||||
|
||||
if (objType == 'function' || (objType == 'object' && obj !== null)) {
|
||||
if (typeof (key = obj.$$hashKey) == 'function') {
|
||||
// must invoke on object to keep the right this
|
||||
if (key) {
|
||||
if (typeof key === 'function') {
|
||||
key = obj.$$hashKey();
|
||||
} else if (key === undefined) {
|
||||
key = obj.$$hashKey = (nextUidFn || nextUid)();
|
||||
}
|
||||
} else {
|
||||
key = obj;
|
||||
return key;
|
||||
}
|
||||
|
||||
return objType + ':' + key;
|
||||
var objType = typeof obj;
|
||||
if (objType == 'function' || (objType == 'object' && obj !== null)) {
|
||||
key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
|
||||
} else {
|
||||
key = objType + ':' + obj;
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user