Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 00456a8f93 | |||
| 0b3022e8e0 | |||
| 456026eff1 | |||
| 8ec3efd967 | |||
| 8528781f85 | |||
| 98f603722d | |||
| ebece0bcb9 | |||
| 4b7398eeca | |||
| 4e79decc30 | |||
| 888b0f5400 | |||
| aaf9c5e598 | |||
| 893d2f8000 | |||
| effc98fdc9 | |||
| 64cdbf3ae9 | |||
| 5bba892ffd | |||
| ec27deacfd | |||
| c6e4defcb6 | |||
| 74a7afcb31 | |||
| 08cc6edd38 | |||
| 4f3870500d | |||
| cd0507bc3a | |||
| 20eef05195 | |||
| a6d7b4bdbd | |||
| 5811c5c35b | |||
| e37e30e93f | |||
| a0b8ab8d7d | |||
| 129c53730c | |||
| 8695138aec | |||
| ed56872bb2 | |||
| cb183433a0 | |||
| 631fbda6a9 | |||
| 4c33b56612 | |||
| 93b0c2d892 | |||
| d262378b7c | |||
| 7729c84ec7 | |||
| dffeef29d7 | |||
| 2e22588ccf | |||
| 0b0acb0342 | |||
| 18fc43e828 | |||
| ed47d811e2 | |||
| a84f9f6178 | |||
| 4fbbe1152e | |||
| df3d941c57 | |||
| 2bd3214a55 | |||
| fc2abef327 | |||
| 76a0eb89fb | |||
| ee57b4c26b | |||
| 29eaabc000 | |||
| 2a6081057f | |||
| 79538afd7b | |||
| bcfa64e77c | |||
| b7a2deed30 | |||
| 98ff901bda | |||
| 428b81cba9 | |||
| 5dae9c230e | |||
| c3fad1157e | |||
| 9242c580a1 | |||
| c2860944c6 | |||
| e9a00fbdc3 | |||
| d018ac2a9a | |||
| b770c353bc | |||
| 812277c257 | |||
| 61871da9de | |||
| 9ee075518f | |||
| afe93eaff8 | |||
| d8f94d1a3f | |||
| f53b53df22 | |||
| eab5731afc | |||
| 986c446aaf | |||
| 60366c8d0b | |||
| 494c8aa0b3 | |||
| cd9459e129 | |||
| 2862883bd8 | |||
| bbb673a48a |
+1
-1
@@ -24,7 +24,7 @@ install:
|
||||
- npm config set spin false
|
||||
# Log HTTP requests
|
||||
- npm config set loglevel http
|
||||
- time ./scripts/travis/fetch_bundle.sh
|
||||
- time ./scripts/travis/npm-bundle-deps.sh
|
||||
- time npm install
|
||||
|
||||
before_script:
|
||||
|
||||
+575
@@ -1,3 +1,548 @@
|
||||
<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 +1045,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)
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.lang-text * {
|
||||
color: #333333!important;
|
||||
}
|
||||
|
||||
.pln {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,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!!!');
|
||||
});
|
||||
|
||||
@@ -68,4 +68,21 @@ describe('docs.angularjs.org', function () {
|
||||
expect(element(by.css('.minerr-errmsg')).getText()).toEqual("Argument 'Missing' is not a function, got undefined");
|
||||
});
|
||||
});
|
||||
|
||||
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+/);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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') {
|
||||
|
||||
@@ -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 $}
|
||||
});
|
||||
|
||||
@@ -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.
|
||||
}]);
|
||||
```
|
||||
@@ -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
|
||||
|
||||
@@ -909,6 +909,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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -382,8 +382,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
|
||||
@@ -391,6 +389,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>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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/
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -59,7 +59,7 @@ 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`
|
||||
@@ -210,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:
|
||||
|
||||
@@ -145,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$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -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`:
|
||||
|
||||
@@ -12,9 +12,9 @@ set -e
|
||||
# before_script:
|
||||
# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash
|
||||
|
||||
CONNECT_URL="http://saucelabs.com/downloads/Sauce-Connect-latest.zip"
|
||||
CONNECT_URL="https://d2nkw87yt5k0to.cloudfront.net/downloads/sc-4.3-linux.tar.gz"
|
||||
CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
|
||||
CONNECT_DOWNLOAD="Sauce_Connect.zip"
|
||||
CONNECT_DOWNLOAD="sc-4.3-linux.tar.gz"
|
||||
|
||||
CONNECT_LOG="$LOGS_DIR/sauce-connect"
|
||||
CONNECT_STDOUT="$LOGS_DIR/sauce-connect.stdout"
|
||||
@@ -24,7 +24,8 @@ CONNECT_STDERR="$LOGS_DIR/sauce-connect.stderr"
|
||||
mkdir -p $CONNECT_DIR
|
||||
cd $CONNECT_DIR
|
||||
curl $CONNECT_URL -o $CONNECT_DOWNLOAD 2> /dev/null 1> /dev/null
|
||||
unzip $CONNECT_DOWNLOAD > /dev/null
|
||||
mkdir sauce-connect
|
||||
tar --extract --file=$CONNECT_DOWNLOAD --strip-components=1 --directory=sauce-connect > /dev/null
|
||||
rm $CONNECT_DOWNLOAD
|
||||
|
||||
SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||
@@ -45,5 +46,5 @@ echo "Starting Sauce Connect in the background, logging into:"
|
||||
echo " $CONNECT_LOG"
|
||||
echo " $CONNECT_STDOUT"
|
||||
echo " $CONNECT_STDERR"
|
||||
java -jar Sauce-Connect.jar $ARGS $SAUCE_USERNAME $SAUCE_ACCESS_KEY \
|
||||
sauce-connect/bin/sc -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY $ARGS -v \
|
||||
--logfile $CONNECT_LOG 2> $CONNECT_STDERR 1> $CONNECT_STDOUT &
|
||||
|
||||
Generated
+1
-1
@@ -502,7 +502,7 @@
|
||||
}
|
||||
},
|
||||
"dgeni-packages": {
|
||||
"version": "0.9.6",
|
||||
"version": "0.9.7",
|
||||
"dependencies": {
|
||||
"lodash": {
|
||||
"version": "2.4.1"
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@
|
||||
"canonical-path": "0.0.2",
|
||||
"winston": "~0.7.2",
|
||||
"dgeni": "^0.3.0",
|
||||
"dgeni-packages": "^0.9.6",
|
||||
"dgeni-packages": "^0.9.7",
|
||||
"gulp-jshint": "~1.4.2",
|
||||
"jshint-stylish": "~0.1.5",
|
||||
"node-html-encoder": "0.0.2",
|
||||
|
||||
+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: {
|
||||
|
||||
@@ -7,9 +7,11 @@ config.sauceKey = process.env.SAUCE_ACCESS_KEY;
|
||||
|
||||
config.multiCapabilities = [{
|
||||
'browserName': 'chrome',
|
||||
'platform': 'OS X 10.9',
|
||||
'name': 'Angular E2E',
|
||||
'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
|
||||
'build': process.env.TRAVIS_BUILD_NUMBER
|
||||
'build': process.env.TRAVIS_BUILD_NUMBER,
|
||||
'version': '34'
|
||||
}, {
|
||||
'browserName': 'firefox',
|
||||
'name': 'Angular E2E',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,4 +6,4 @@ set -e
|
||||
cd $(dirname $0);
|
||||
|
||||
cd ../..
|
||||
curl "http://23.251.148.50:8000/tar/$TRAVIS_REPO_SLUG/$TRAVIS_COMMIT" | tar xz
|
||||
curl "http://23.251.148.50:8000/tar/$TRAVIS_REPO_SLUG/$TRAVIS_COMMIT" | tar xz || true
|
||||
+14
-5
@@ -820,9 +820,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) {
|
||||
result = copy(source[key], null, stackSource, stackDest);
|
||||
if (isObject(source[key])) {
|
||||
@@ -907,7 +911,8 @@ function equals(o1, o2) {
|
||||
return true;
|
||||
}
|
||||
} else if (isDate(o1)) {
|
||||
return isDate(o2) && o1.getTime() == o2.getTime();
|
||||
if (!isDate(o2)) return false;
|
||||
return (isNaN(o1.getTime()) && isNaN(o2.getTime())) || (o1.getTime() === o2.getTime());
|
||||
} else if (isRegExp(o1) && isRegExp(o2)) {
|
||||
return o1.toString() == o2.toString();
|
||||
} else {
|
||||
@@ -1345,7 +1350,11 @@ function bootstrap(element, modules) {
|
||||
|
||||
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 || [];
|
||||
|
||||
@@ -109,11 +109,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,
|
||||
|
||||
+28
-12
@@ -952,21 +952,37 @@ forEach({
|
||||
|
||||
clone: jqLiteClone,
|
||||
|
||||
triggerHandler: function(element, eventName, eventData) {
|
||||
// Copy event handlers in case event handlers array is modified during execution.
|
||||
var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName],
|
||||
eventFnsCopy = shallowCopy(eventFns || []);
|
||||
triggerHandler: function(element, event, extraParameters) {
|
||||
|
||||
eventData = eventData || [];
|
||||
var dummyEvent, eventFnsCopy, handlerArgs;
|
||||
var eventName = event.type || event;
|
||||
var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName];
|
||||
|
||||
var event = [{
|
||||
preventDefault: noop,
|
||||
stopPropagation: noop
|
||||
}];
|
||||
if (eventFns) {
|
||||
|
||||
forEach(eventFnsCopy, function(fn) {
|
||||
fn.apply(element, event.concat(eventData));
|
||||
});
|
||||
// Create a dummy event to pass to the handlers
|
||||
dummyEvent = {
|
||||
preventDefault: function() { this.defaultPrevented = true; },
|
||||
isDefaultPrevented: function() { return this.defaultPrevented === true; },
|
||||
stopPropagation: noop,
|
||||
type: eventName,
|
||||
target: element
|
||||
};
|
||||
|
||||
// If a custom event was provided then extend our dummy event with it
|
||||
if (event.type) {
|
||||
dummyEvent = extend(dummyEvent, event);
|
||||
}
|
||||
|
||||
// Copy event handlers in case event handlers array is modified during execution.
|
||||
eventFnsCopy = shallowCopy(eventFns);
|
||||
handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
|
||||
|
||||
forEach(eventFnsCopy, function(fn) {
|
||||
fn.apply(element, handlerArgs);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}, function(fn, name){
|
||||
/**
|
||||
|
||||
+3
-2
@@ -114,7 +114,7 @@ function setupModuleLoader(window) {
|
||||
* @ngdoc property
|
||||
* @name angular.Module#requires
|
||||
* @module ng
|
||||
* @returns {Array.<string>} List of module names which must be loaded before this module.
|
||||
*
|
||||
* @description
|
||||
* Holds the list of modules which the injector will load before the current module is
|
||||
* loaded.
|
||||
@@ -125,8 +125,9 @@ function setupModuleLoader(window) {
|
||||
* @ngdoc property
|
||||
* @name angular.Module#name
|
||||
* @module ng
|
||||
* @returns {string} Name of the module.
|
||||
*
|
||||
* @description
|
||||
* Name of the module.
|
||||
*/
|
||||
name: name,
|
||||
|
||||
|
||||
@@ -67,8 +67,10 @@
|
||||
$scope.keys = [];
|
||||
$scope.cache = $cacheFactory('cacheId');
|
||||
$scope.put = function(key, value) {
|
||||
$scope.cache.put(key, value);
|
||||
$scope.keys.push(key);
|
||||
if ($scope.cache.get(key) === undefined) {
|
||||
$scope.keys.push(key);
|
||||
}
|
||||
$scope.cache.put(key, value === undefined ? null : value);
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
|
||||
+9
-7
@@ -191,9 +191,9 @@
|
||||
*
|
||||
* * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
|
||||
* * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
|
||||
* * `^` - Locate the required controller by searching the element's parents. Throw an error if not found.
|
||||
* * `?^` - Attempt to locate the required controller by searching the element's parents or pass `null` to the
|
||||
* `link` fn if not found.
|
||||
* * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
|
||||
* * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
|
||||
* `null` to the `link` fn if not found.
|
||||
*
|
||||
*
|
||||
* #### `controllerAs`
|
||||
@@ -214,7 +214,7 @@
|
||||
*
|
||||
* #### `template`
|
||||
* HTML markup that may:
|
||||
* * Replace the contents of the directive's element (defualt).
|
||||
* * Replace the contents of the directive's element (default).
|
||||
* * Replace the directive's element itself (if `replace` is true - DEPRECATED).
|
||||
* * Wrap the contents of the directive's element (if `transclude` is true).
|
||||
*
|
||||
@@ -1474,7 +1474,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (parentGet.literal) {
|
||||
compare = equals;
|
||||
} else {
|
||||
compare = function(a,b) { return a === b; };
|
||||
compare = function(a,b) { return a === b || (a !== a && b !== b); };
|
||||
}
|
||||
parentSet = parentGet.assign || function() {
|
||||
// reset the change, or we will throw this exception on every $digest
|
||||
@@ -2011,8 +2011,10 @@ function directiveNormalize(name) {
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $compile.directive.Attributes#$attr
|
||||
* @returns {object} A map of DOM element attribute names to the normalized name. This is
|
||||
* needed to do reverse lookup from normalized name back to actual name.
|
||||
*
|
||||
* @description
|
||||
* A map of DOM element attribute names to the normalized name. This is
|
||||
* needed to do reverse lookup from normalized name back to actual name.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
|
||||
* We shouldn't do this, because it will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
|
||||
* ```html
|
||||
* <div ng-init="scope = { isDisabled: false }">
|
||||
* <button disabled="{{scope.isDisabled}}">Disabled</button>
|
||||
@@ -380,8 +380,12 @@ forEach(['src', 'srcset', 'href'], function(attrName) {
|
||||
}
|
||||
|
||||
attr.$observe(normalized, function(value) {
|
||||
if (!value)
|
||||
return;
|
||||
if (!value) {
|
||||
if (attrName === 'href') {
|
||||
attr.$set(name, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
attr.$set(name, value);
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ var inputType = {
|
||||
* @name input[text]
|
||||
*
|
||||
* @description
|
||||
* Standard HTML text input with angular data binding.
|
||||
* Standard HTML text input with angular data binding, inherited by most of the `input` elements.
|
||||
*
|
||||
* *NOTE* Not every feature offered is available for all input types.
|
||||
*
|
||||
* @param {string} ngModel Assignable angular expression to data-bind to.
|
||||
* @param {string=} name Property name of the form under which the control is published.
|
||||
@@ -35,6 +37,8 @@ var inputType = {
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
* @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
|
||||
* This parameter is ignored for input[type=password] controls, which will never trim the
|
||||
* input.
|
||||
*
|
||||
* @example
|
||||
<example name="text-input-directive" module="textInputExample">
|
||||
@@ -474,6 +478,7 @@ function addNativeHtml5Validators(ctrl, validatorName, badFlags, ignoreFlags, va
|
||||
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
var validity = element.prop(VALIDITY_STATE_PROPERTY);
|
||||
var placeholder = element[0].placeholder, noevent = {};
|
||||
var type = lowercase(element[0].type);
|
||||
ctrl.$$validityState = validity;
|
||||
|
||||
// In composition mode, users are still inputing intermediate text buffer,
|
||||
@@ -507,8 +512,8 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
|
||||
// By default we will trim the value
|
||||
// If the attribute ng-trim exists we will avoid trimming
|
||||
// e.g. <input ng-model="foo" ng-trim="false">
|
||||
if (toBoolean(attr.ngTrim || 'T')) {
|
||||
// If input type is 'password', the value is never trimmed
|
||||
if (type !== 'password' && (toBoolean(attr.ngTrim || 'T'))) {
|
||||
value = trim(value);
|
||||
}
|
||||
|
||||
@@ -783,6 +788,8 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
||||
* HTML input element control with angular data-binding. Input control follows HTML5 input types
|
||||
* and polyfills the HTML5 validation behavior for older browsers.
|
||||
*
|
||||
* *NOTE* Not every feature offered is available for all input types.
|
||||
*
|
||||
* @param {string} ngModel Assignable angular expression to data-bind to.
|
||||
* @param {string=} name Property name of the form under which the control is published.
|
||||
* @param {string=} required Sets `required` validation error key if the value is not entered.
|
||||
@@ -796,6 +803,9 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
||||
* patterns defined as scope expressions.
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
* @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
|
||||
* This parameter is ignored for input[type=password] controls, which will never trim the
|
||||
* input.
|
||||
*
|
||||
* @example
|
||||
<example name="input-directive" module="inputExample">
|
||||
|
||||
@@ -150,7 +150,6 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
|
||||
* @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
|
||||
*
|
||||
* @example
|
||||
Try it here: enter text in text box and watch the greeting change.
|
||||
|
||||
<example module="bindHtmlExample" deps="angular-sanitize.js">
|
||||
<file name="index.html">
|
||||
|
||||
@@ -307,6 +307,13 @@ forEach(
|
||||
* server and reloading the current page), but only if the form does not contain `action`,
|
||||
* `data-action`, or `x-action` attributes.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
|
||||
* `ngSubmit` handlers together. See the
|
||||
* {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
|
||||
* for a detailed discussion of when `ngSubmit` may be triggered.
|
||||
* </div>
|
||||
*
|
||||
* @element form
|
||||
* @priority 0
|
||||
* @param {expression} ngSubmit {@link guide/expression Expression} to eval.
|
||||
|
||||
+27
-21
@@ -395,21 +395,37 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
value = valueFn(scope, locals);
|
||||
}
|
||||
}
|
||||
// Update the null option's selected property here so $render cleans it up correctly
|
||||
if (optionGroupsCache[0].length > 1) {
|
||||
if (optionGroupsCache[0][1].id !== key) {
|
||||
optionGroupsCache[0][1].selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctrl.$setViewValue(value);
|
||||
render();
|
||||
});
|
||||
});
|
||||
|
||||
ctrl.$render = render;
|
||||
|
||||
// TODO(vojta): can't we optimize this ?
|
||||
scope.$watch(render);
|
||||
scope.$watchCollection(valuesFn, render);
|
||||
if ( multiple ) {
|
||||
scope.$watchCollection(function() { return ctrl.$modelValue; }, render);
|
||||
}
|
||||
|
||||
function getSelectedSet() {
|
||||
var selectedSet = false;
|
||||
if (multiple) {
|
||||
var modelValue = ctrl.$modelValue;
|
||||
if (trackFn && isArray(modelValue)) {
|
||||
selectedSet = new HashMap([]);
|
||||
var locals = {};
|
||||
for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
|
||||
locals[valueName] = modelValue[trackIndex];
|
||||
selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
|
||||
}
|
||||
} else {
|
||||
selectedSet = new HashMap(modelValue);
|
||||
}
|
||||
}
|
||||
return selectedSet;
|
||||
}
|
||||
|
||||
|
||||
function render() {
|
||||
// Temporary location for the option groups before we render them
|
||||
@@ -427,22 +443,11 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
groupIndex, index,
|
||||
locals = {},
|
||||
selected,
|
||||
selectedSet = false, // nothing is selected yet
|
||||
selectedSet = getSelectedSet(),
|
||||
lastElement,
|
||||
element,
|
||||
label;
|
||||
|
||||
if (multiple) {
|
||||
if (trackFn && isArray(modelValue)) {
|
||||
selectedSet = new HashMap([]);
|
||||
for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
|
||||
locals[valueName] = modelValue[trackIndex];
|
||||
selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
|
||||
}
|
||||
} else {
|
||||
selectedSet = new HashMap(modelValue);
|
||||
}
|
||||
}
|
||||
|
||||
// We now build up the list of options we need (we merge later)
|
||||
for (index = 0; length = keys.length, index < length; index++) {
|
||||
@@ -538,7 +543,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
lastElement.val(existingOption.id = option.id);
|
||||
}
|
||||
// lastElement.prop('selected') provided by jQuery has side-effects
|
||||
if (existingOption.selected !== option.selected) {
|
||||
if (lastElement[0].selected !== option.selected) {
|
||||
lastElement.prop('selected', (existingOption.selected = option.selected));
|
||||
if (msie) {
|
||||
// See #7692
|
||||
@@ -561,6 +566,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
(element = optionTemplate.clone())
|
||||
.val(option.id)
|
||||
.prop('selected', option.selected)
|
||||
.attr('selected', option.selected)
|
||||
.text(option.label);
|
||||
}
|
||||
|
||||
|
||||
@@ -327,7 +327,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
* (e.g. `"h 'o''clock'"`).
|
||||
*
|
||||
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
|
||||
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
|
||||
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
|
||||
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
|
||||
* specified in the string input, the time is considered to be in the local timezone.
|
||||
* @param {string=} format Formatting rules (see Description). If not specified,
|
||||
|
||||
@@ -18,9 +18,13 @@
|
||||
*
|
||||
* - `function`: Getter function. The result of this function will be sorted using the
|
||||
* `<`, `=`, `>` operator.
|
||||
* - `string`: An Angular expression which evaluates to an object to order by, such as 'name'
|
||||
* to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control
|
||||
* ascending or descending sort order (for example, +name or -name).
|
||||
* - `string`: An Angular expression. The result of this expression is used to compare elements
|
||||
* (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
|
||||
* 3 first characters of a property called `name`). The result of a constant expression
|
||||
* is interpreted as a property name to be used in comparisons (for example `"special name"`
|
||||
* to sort object by the value of their `special name` property). An expression can be
|
||||
* optionally prefixed with `+` or `-` to control ascending or descending sort order
|
||||
* (for example, `+name` or `-name`).
|
||||
* - `Array`: An array of function or string predicates. The first predicate in the array
|
||||
* is used for sorting, but when two items are equivalent, the next predicate is used.
|
||||
*
|
||||
|
||||
+5
-3
@@ -275,6 +275,7 @@ function $HttpProvider() {
|
||||
* - {@link ng.$http#put $http.put}
|
||||
* - {@link ng.$http#delete $http.delete}
|
||||
* - {@link ng.$http#jsonp $http.jsonp}
|
||||
* - {@link ng.$http#patch $http.patch}
|
||||
*
|
||||
*
|
||||
* # Setting HTTP Headers
|
||||
@@ -539,7 +540,7 @@ function $HttpProvider() {
|
||||
* that only JavaScript running on your domain could have sent the request. The token must be
|
||||
* unique for each user and must be verifiable by the server (to prevent the JavaScript from
|
||||
* making up its own tokens). We recommend that the token is a digest of your site's
|
||||
* authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
|
||||
* authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
|
||||
* for added security.
|
||||
*
|
||||
* The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
|
||||
@@ -576,7 +577,7 @@ function $HttpProvider() {
|
||||
* - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
|
||||
* that should abort the request when resolved.
|
||||
* - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
|
||||
* XHR object. See [requests with credentials]https://developer.mozilla.org/en/http_access_control#section_5
|
||||
* XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
|
||||
* for more information.
|
||||
* - **responseType** - `{string}` - see
|
||||
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
|
||||
@@ -945,7 +946,8 @@ function $HttpProvider() {
|
||||
promise.then(removePendingReq, removePendingReq);
|
||||
|
||||
|
||||
if ((config.cache || defaults.cache) && config.cache !== false && config.method == 'GET') {
|
||||
if ((config.cache || defaults.cache) && config.cache !== false &&
|
||||
(config.method === 'GET' || config.method === 'JSONP')) {
|
||||
cache = isObject(config.cache) ? config.cache
|
||||
: isObject(defaults.cache) ? defaults.cache
|
||||
: defaultCache;
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ function $IntervalProvider() {
|
||||
* };
|
||||
*
|
||||
* $scope.$on('$destroy', function() {
|
||||
* // Make sure that the interval nis destroyed too
|
||||
* // Make sure that the interval is destroyed too
|
||||
* $scope.stopFight();
|
||||
* });
|
||||
* }])
|
||||
|
||||
+7
-1
@@ -635,6 +635,8 @@ function $LocationProvider(){
|
||||
$location = new LocationMode(appBase, '#' + hashPrefix);
|
||||
$location.$$parse($location.$$rewrite(initialUrl));
|
||||
|
||||
var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
|
||||
|
||||
$rootElement.on('click', function(event) {
|
||||
// TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
|
||||
// currently we open nice url link and redirect then
|
||||
@@ -657,6 +659,9 @@ function $LocationProvider(){
|
||||
absHref = urlResolve(absHref.animVal).href;
|
||||
}
|
||||
|
||||
// Ignore when url is started with javascript: or mailto:
|
||||
if (IGNORE_URI_REGEXP.test(absHref)) return;
|
||||
|
||||
// Make relative links work in HTML5 mode for legacy browsers (or at least IE8 & 9)
|
||||
// The href should be a regular url e.g. /link/somewhere or link/somewhere or ../somewhere or
|
||||
// somewhere#anchor or http://example.com/somewhere
|
||||
@@ -665,7 +670,7 @@ function $LocationProvider(){
|
||||
// http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
|
||||
var href = elm.attr('href') || elm.attr('xlink:href');
|
||||
|
||||
if (href.indexOf('://') < 0) { // Ignore absolute URLs
|
||||
if (href && href.indexOf('://') < 0) { // Ignore absolute URLs
|
||||
var prefix = '#' + hashPrefix;
|
||||
if (href[0] == '/') {
|
||||
// absolute path - replace old path
|
||||
@@ -677,6 +682,7 @@ function $LocationProvider(){
|
||||
// relative path - join with current path
|
||||
var stack = $location.path().split("/"),
|
||||
parts = href.split("/");
|
||||
if (stack.length === 2 && !stack[1]) stack.length = 1;
|
||||
for (var i=0; i<parts.length; i++) {
|
||||
if (parts[i] == ".")
|
||||
continue;
|
||||
|
||||
+9
-6
@@ -606,9 +606,9 @@ Parser.prototype = {
|
||||
var middle;
|
||||
var token;
|
||||
if ((token = this.expect('?'))) {
|
||||
middle = this.ternary();
|
||||
middle = this.assignment();
|
||||
if ((token = this.expect(':'))) {
|
||||
return this.ternaryFn(left, middle, this.ternary());
|
||||
return this.ternaryFn(left, middle, this.assignment());
|
||||
} else {
|
||||
this.throwError('expected :', token);
|
||||
}
|
||||
@@ -696,7 +696,9 @@ Parser.prototype = {
|
||||
return getter(self || object(scope, locals));
|
||||
}, {
|
||||
assign: function(scope, value, locals) {
|
||||
return setter(object(scope, locals), field, value, parser.text, parser.options);
|
||||
var o = object(scope, locals);
|
||||
if (!o) object.assign(scope, o = {});
|
||||
return setter(o, field, value, parser.text, parser.options);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -726,10 +728,11 @@ Parser.prototype = {
|
||||
return v;
|
||||
}, {
|
||||
assign: function(self, value, locals) {
|
||||
var key = indexFn(self, locals);
|
||||
var key = ensureSafeMemberName(indexFn(self, locals), parser.text);
|
||||
// prevent overwriting of Function.constructor which would break ensureSafeObject check
|
||||
var safe = ensureSafeObject(obj(self, locals), parser.text);
|
||||
return safe[key] = value;
|
||||
var o = ensureSafeObject(obj(self, locals), parser.text);
|
||||
if (!o) obj.assign(self, o = {});
|
||||
return o[key] = value;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
function $$SanitizeUriProvider() {
|
||||
var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
|
||||
imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
|
||||
imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file):|data:image\/)/;
|
||||
|
||||
/**
|
||||
* @description
|
||||
|
||||
Vendored
+3
-1
@@ -1779,7 +1779,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
||||
* use the `passThrough` request handler of `when` instead of `respond`.
|
||||
*
|
||||
* Additionally, we don't want to manually have to flush mocked out requests like we do during unit
|
||||
* testing. For this reason the e2e $httpBackend automatically flushes mocked out requests
|
||||
* testing. For this reason the e2e $httpBackend flushes mocked out requests
|
||||
* automatically, closely simulating the behavior of the XMLHttpRequest object.
|
||||
*
|
||||
* To setup the application to run with this http backend, you have to create a module that depends
|
||||
@@ -1996,6 +1996,7 @@ if(window.jasmine || window.mocha) {
|
||||
* @description
|
||||
*
|
||||
* *NOTE*: This function is also published on window for easy access.<br>
|
||||
* *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
|
||||
*
|
||||
* This function registers a module configuration code. It collects the configuration information
|
||||
* which will be used when the injector is created by {@link angular.mock.inject inject}.
|
||||
@@ -2038,6 +2039,7 @@ if(window.jasmine || window.mocha) {
|
||||
* @description
|
||||
*
|
||||
* *NOTE*: This function is also published on window for easy access.<br>
|
||||
* *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
|
||||
*
|
||||
* The inject function wraps a function into an injectable function. The inject() creates new
|
||||
* instance of {@link auto.$injector $injector} per test, which is then used for
|
||||
|
||||
@@ -94,8 +94,10 @@ function shallowClearAndCopy(src, dst) {
|
||||
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
|
||||
* URL `/path/greet?salutation=Hello`.
|
||||
*
|
||||
* If the parameter value is prefixed with `@` then the value of that parameter will be taken
|
||||
* from the corresponding key on the data object (useful for non-GET operations).
|
||||
* If the parameter value is prefixed with `@` then the value for that parameter will be extracted
|
||||
* from the corresponding property on the `data` object (provided when calling an action method). For
|
||||
* example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
|
||||
* will be `data.someProp`.
|
||||
*
|
||||
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend
|
||||
* the default set of resource actions. The declaration should be created in the format of {@link
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
*/
|
||||
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
var LINKY_URL_REGEXP =
|
||||
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/,
|
||||
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"]/,
|
||||
MAILTO_REGEXP = /^mailto:/;
|
||||
|
||||
return function(text, target) {
|
||||
|
||||
@@ -232,6 +232,13 @@ function makeMap(str) {
|
||||
* @param {object} handler
|
||||
*/
|
||||
function htmlParser( html, handler ) {
|
||||
if (typeof html !== 'string') {
|
||||
if (html === null || typeof html === 'undefined') {
|
||||
html = '';
|
||||
} else {
|
||||
html = '' + html;
|
||||
}
|
||||
}
|
||||
var index, chars, match, stack = [], last = html, text;
|
||||
stack.last = function() { return stack[ stack.length - 1 ]; };
|
||||
|
||||
|
||||
+12
-1
@@ -174,6 +174,12 @@ describe('angular', function() {
|
||||
expect(aCopy).toBe(aCopy.self);
|
||||
expect(aCopy.selfs[2]).not.toBe(a.selfs[2]);
|
||||
});
|
||||
|
||||
it('should clear destination arrays correctly when source is non-array', function() {
|
||||
expect(copy(null, [1,2,3])).toEqual([]);
|
||||
expect(copy(undefined, [1,2,3])).toEqual([]);
|
||||
expect(copy({0: 1, 1: 2}, [1,2,3])).toEqual([1,2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("extend", function() {
|
||||
@@ -360,6 +366,11 @@ describe('angular', function() {
|
||||
expect(equals(new Date(0), new Date(1))).toBe(false);
|
||||
expect(equals(new Date(0), 0)).toBe(false);
|
||||
expect(equals(0, new Date(0))).toBe(false);
|
||||
|
||||
expect(equals(new Date(undefined), new Date(undefined))).toBe(true);
|
||||
expect(equals(new Date(undefined), new Date(0))).toBe(false);
|
||||
expect(equals(new Date(undefined), new Date(null))).toBe(false);
|
||||
expect(equals(new Date(undefined), new Date('wrong'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should correctly test for keys that are present on Object.prototype', function() {
|
||||
@@ -822,7 +833,7 @@ describe('angular', function() {
|
||||
expect(function () {
|
||||
angular.bootstrap(element);
|
||||
}).toThrowMatching(
|
||||
/\[ng:btstrpd\] App Already Bootstrapped with this Element '<div class="?ng\-scope"?( ng[0-9]+="?[0-9]+"?)?>'/i
|
||||
/\[ng:btstrpd\] App Already Bootstrapped with this Element '<div class="?ng\-scope"?( ng[0-9]+="?[0-9]+"?)?>'/i
|
||||
);
|
||||
|
||||
dealoc(element);
|
||||
|
||||
+21
-1
@@ -1647,9 +1647,11 @@ describe('jqLite', function() {
|
||||
element.triggerHandler('click');
|
||||
event = pokeSpy.mostRecentCall.args[0];
|
||||
expect(event.preventDefault).toBeDefined();
|
||||
expect(event.target).toEqual(element[0]);
|
||||
expect(event.type).toEqual('click');
|
||||
});
|
||||
|
||||
it('should pass data as an additional argument', function() {
|
||||
it('should pass extra parameters as an additional argument', function() {
|
||||
var element = jqLite('<a>poke</a>'),
|
||||
pokeSpy = jasmine.createSpy('poke'),
|
||||
data;
|
||||
@@ -1680,6 +1682,24 @@ describe('jqLite', function() {
|
||||
expect(clickOnceSpy).toHaveBeenCalledOnce();
|
||||
expect(clickSpy.callCount).toBe(2);
|
||||
});
|
||||
|
||||
it("should accept a custom event instead of eventName", function() {
|
||||
var element = jqLite('<a>poke</a>'),
|
||||
pokeSpy = jasmine.createSpy('poke'),
|
||||
customEvent = {
|
||||
type: 'click',
|
||||
someProp: 'someValue'
|
||||
},
|
||||
actualEvent;
|
||||
|
||||
element.on('click', pokeSpy);
|
||||
element.triggerHandler(customEvent);
|
||||
actualEvent = pokeSpy.mostRecentCall.args[0];
|
||||
expect(actualEvent.preventDefault).toBeDefined();
|
||||
expect(actualEvent.someProp).toEqual('someValue');
|
||||
expect(actualEvent.target).toEqual(element[0]);
|
||||
expect(actualEvent.type).toEqual('click');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -2671,6 +2671,28 @@ describe('$compile', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should update parent scope when "="-bound NaN changes', inject(function($compile, $rootScope) {
|
||||
$rootScope.num = NaN;
|
||||
compile('<div my-component reference="num"></div>');
|
||||
var isolateScope = element.isolateScope();
|
||||
expect(isolateScope.reference).toBeNaN();
|
||||
|
||||
isolateScope.$apply(function(scope) { scope.reference = 64; });
|
||||
expect($rootScope.num).toBe(64);
|
||||
}));
|
||||
|
||||
|
||||
it('should update isolate scope when "="-bound NaN changes', inject(function($compile, $rootScope) {
|
||||
$rootScope.num = NaN;
|
||||
compile('<div my-component reference="num"></div>');
|
||||
var isolateScope = element.isolateScope();
|
||||
expect(isolateScope.reference).toBeNaN();
|
||||
|
||||
$rootScope.$apply(function(scope) { scope.num = 64; });
|
||||
expect(isolateScope.reference).toBe(64);
|
||||
}));
|
||||
|
||||
|
||||
describe('attribute', function() {
|
||||
it('should copy simple attribute', inject(function() {
|
||||
compile('<div><span my-component attr="some text">');
|
||||
|
||||
@@ -252,6 +252,23 @@ describe('ngHref', function() {
|
||||
expect(element.attr('href')).toEqual('http://server');
|
||||
}));
|
||||
|
||||
it('should not set the href if ng-href is empty', inject(function($rootScope, $compile) {
|
||||
$rootScope.url = null;
|
||||
element = $compile('<a ng-href="{{url}}"></a>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('href')).toEqual(undefined);
|
||||
}));
|
||||
|
||||
it('should remove the href if ng-href changes to empty', inject(function($rootScope, $compile) {
|
||||
$rootScope.url = 'http://www.google.com/';
|
||||
element = $compile('<a ng-href="{{url}}"></a>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
$rootScope.url = null;
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('href')).toEqual(undefined);
|
||||
}));
|
||||
|
||||
if (isDefined(window.SVGElement)) {
|
||||
describe('SVGAElement', function() {
|
||||
it('should interpolate the expression and bind to xlink:href', inject(function($compile, $rootScope) {
|
||||
|
||||
@@ -1546,6 +1546,30 @@ describe('input', function() {
|
||||
expect(scope.items[0].selected).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('password', function() {
|
||||
// Under no circumstances should input[type=password] trim inputs
|
||||
it('should not trim if ngTrim is unspecified', function() {
|
||||
compileInput('<input type="password" ng-model="password">');
|
||||
changeInputValueTo(' - - untrimmed - - ');
|
||||
expect(scope.password.length).toBe(' - - untrimmed - - '.length);
|
||||
});
|
||||
|
||||
|
||||
it('should not trim if ngTrim !== false', function() {
|
||||
compileInput('<input type="password" ng-model="password" ng-trim="true">');
|
||||
changeInputValueTo(' - - untrimmed - - ');
|
||||
expect(scope.password.length).toBe(' - - untrimmed - - '.length);
|
||||
});
|
||||
|
||||
|
||||
it('should not trim if ngTrim === false', function() {
|
||||
compileInput('<input type="password" ng-model="password" ng-trim="false">');
|
||||
changeInputValueTo(' - - untrimmed - - ');
|
||||
expect(scope.password.length).toBe(' - - untrimmed - - '.length);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('NgModel animations', function() {
|
||||
|
||||
@@ -735,6 +735,8 @@ describe('select', function() {
|
||||
|
||||
it('should not update selected property of an option element on digest with no change event',
|
||||
function() {
|
||||
// ng-options="value.name for value in values"
|
||||
// ng-model="selected"
|
||||
createSingleSelect();
|
||||
|
||||
scope.$apply(function() {
|
||||
@@ -743,6 +745,11 @@ describe('select', function() {
|
||||
});
|
||||
|
||||
var options = element.find('option');
|
||||
|
||||
expect(scope.selected).toEqual({ name: 'A' });
|
||||
expect(options.eq(0).prop('selected')).toBe(true);
|
||||
expect(options.eq(1).prop('selected')).toBe(false);
|
||||
|
||||
var optionToSelect = options.eq(1);
|
||||
|
||||
expect(optionToSelect.text()).toBe('B');
|
||||
@@ -987,6 +994,56 @@ describe('select', function() {
|
||||
expect(element.find('option').eq(0).prop('selected')).toBeTruthy();
|
||||
expect(element.find('option').length).toEqual(2);
|
||||
});
|
||||
|
||||
|
||||
it('should ensure that at least one option element has the "selected" attribute', function() {
|
||||
function countSelected() {
|
||||
var count = 0;
|
||||
forEach(element.find('option'), function(option) {
|
||||
count += option.getAttribute('selected') ? 1 : 0;
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
createSelect({
|
||||
'ng-model': 'selected',
|
||||
'ng-options': 'item.id as item.name for item in values'
|
||||
});
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.values = [{id: 10, name: 'A'}, {id: 20, name: 'B'}];
|
||||
});
|
||||
expect(element.val()).toEqual('?');
|
||||
expect(countSelected()).toEqual(1);
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.selected = 10;
|
||||
});
|
||||
// Here the ? option should disappear and the first real option should have selected attribute
|
||||
expect(element.val()).toEqual('0');
|
||||
expect(countSelected()).toEqual(1);
|
||||
|
||||
// Here the selected value is changed but we don't change the selected attribute
|
||||
scope.$apply(function() {
|
||||
scope.selected = 20;
|
||||
});
|
||||
expect(element.val()).toEqual('1');
|
||||
expect(countSelected()).toEqual(1);
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.values.push({id: 30, name: 'C'});
|
||||
});
|
||||
expect(element.val()).toEqual('1');
|
||||
expect(countSelected()).toEqual(1);
|
||||
|
||||
// Here the ? option should reappear and have selected attribute
|
||||
scope.$apply(function() {
|
||||
scope.selected = undefined;
|
||||
});
|
||||
expect(element.val()).toEqual('?');
|
||||
expect(countSelected()).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1134,8 +1191,73 @@ describe('select', function() {
|
||||
browserTrigger(element, 'change');
|
||||
expect(scope.selected).toEqual(null);
|
||||
});
|
||||
|
||||
|
||||
// Regression https://github.com/angular/angular.js/issues/7855
|
||||
it('should update the model with ng-change', function() {
|
||||
createSelect({
|
||||
'ng-change':'change()',
|
||||
'ng-model':'selected',
|
||||
'ng-options':'value for value in values'
|
||||
});
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.values = ['A', 'B'];
|
||||
scope.selected = 'A';
|
||||
});
|
||||
|
||||
scope.change = function() {
|
||||
scope.selected = 'A';
|
||||
};
|
||||
|
||||
element.find('option')[1].selected = true;
|
||||
|
||||
browserTrigger(element, 'change');
|
||||
expect(element.find('option')[0].selected).toBeTruthy();
|
||||
expect(scope.selected).toEqual('A');
|
||||
});
|
||||
});
|
||||
|
||||
describe('disabled blank', function() {
|
||||
it('should select disabled blank by default', function() {
|
||||
var html = '<select ng-model="someModel" ng-options="c for c in choices">' +
|
||||
'<option value="" disabled>Choose One</option>' +
|
||||
'</select>';
|
||||
scope.$apply(function() {
|
||||
scope.choices = ['A', 'B', 'C'];
|
||||
});
|
||||
|
||||
compile(html);
|
||||
|
||||
var options = element.find('option');
|
||||
var optionToSelect = options.eq(0);
|
||||
expect(optionToSelect.text()).toBe('Choose One');
|
||||
expect(optionToSelect.prop('selected')).toBe(true);
|
||||
expect(element[0].value).toBe('');
|
||||
|
||||
dealoc(element);
|
||||
});
|
||||
|
||||
|
||||
it('should select disabled blank by default when select is required', function() {
|
||||
var html = '<select ng-model="someModel" ng-options="c for c in choices" required>' +
|
||||
'<option value="" disabled>Choose One</option>' +
|
||||
'</select>';
|
||||
scope.$apply(function() {
|
||||
scope.choices = ['A', 'B', 'C'];
|
||||
});
|
||||
|
||||
compile(html);
|
||||
|
||||
var options = element.find('option');
|
||||
var optionToSelect = options.eq(0);
|
||||
expect(optionToSelect.text()).toBe('Choose One');
|
||||
expect(optionToSelect.prop('selected')).toBe(true);
|
||||
expect(element[0].value).toBe('');
|
||||
|
||||
dealoc(element);
|
||||
});
|
||||
});
|
||||
|
||||
describe('select-many', function() {
|
||||
|
||||
@@ -1183,6 +1305,7 @@ describe('select', function() {
|
||||
expect(scope.selected).toEqual([scope.values[0]]);
|
||||
});
|
||||
|
||||
|
||||
it('should select from object', function() {
|
||||
createSelect({
|
||||
'ng-model':'selected',
|
||||
|
||||
@@ -1217,6 +1217,18 @@ describe('$http', function() {
|
||||
expect(callback.mostRecentCall.args[0]).toBe('content');
|
||||
}));
|
||||
|
||||
it('should cache JSONP request when cache is provided', inject(function($rootScope) {
|
||||
$httpBackend.expect('JSONP', '/url?cb=JSON_CALLBACK').respond('content');
|
||||
$http({method: 'JSONP', url: '/url?cb=JSON_CALLBACK', cache: cache});
|
||||
$httpBackend.flush();
|
||||
|
||||
$http({method: 'JSONP', url: '/url?cb=JSON_CALLBACK', cache: cache}).success(callback);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
expect(callback.mostRecentCall.args[0]).toBe('content');
|
||||
}));
|
||||
|
||||
it('should cache request when cache is provided and no method specified', function () {
|
||||
doFirstCacheRequest();
|
||||
|
||||
|
||||
+66
-6
@@ -811,15 +811,21 @@ describe('$location', function() {
|
||||
attrs = attrs ? ' ' + attrs + ' ' : '';
|
||||
|
||||
// fake the base behavior
|
||||
if (!relLink) {
|
||||
if (linkHref[0] == '/') {
|
||||
linkHref = 'http://host.com' + linkHref;
|
||||
} else if(!linkHref.match(/:\/\//)) {
|
||||
linkHref = 'http://host.com/base/' + linkHref;
|
||||
if (typeof linkHref === 'string') {
|
||||
if (!relLink) {
|
||||
if (linkHref[0] == '/') {
|
||||
linkHref = 'http://host.com' + linkHref;
|
||||
} else if(!linkHref.match(/:\/\//)) {
|
||||
linkHref = 'http://host.com/base/' + linkHref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
link = jqLite('<a href="' + linkHref + '"' + attrs + '>' + content + '</a>')[0];
|
||||
if (linkHref) {
|
||||
link = jqLite('<a href="' + linkHref + '"' + attrs + '>' + content + '</a>')[0];
|
||||
} else {
|
||||
link = jqLite('<a ' + attrs + '>' + content + '</a>')[0];
|
||||
}
|
||||
|
||||
$provide.value('$sniffer', {history: supportHist});
|
||||
$locationProvider.html5Mode(html5Mode);
|
||||
@@ -934,6 +940,34 @@ describe('$location', function() {
|
||||
});
|
||||
|
||||
|
||||
// Regression (gh-7721)
|
||||
it('should not throw when clicking anchor with no href attribute when history enabled on old browser', function() {
|
||||
configureService(null, true, false);
|
||||
inject(
|
||||
initBrowser(),
|
||||
initLocation(),
|
||||
function($browser) {
|
||||
browserTrigger(link, 'click');
|
||||
expectNoRewrite($browser);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('should produce relative paths correctly when $location.path() is "/" when history enabled on old browser', function() {
|
||||
configureService('partial1', true, false, true);
|
||||
inject(
|
||||
initBrowser(),
|
||||
initLocation(),
|
||||
function($browser, $location) {
|
||||
$location.path('/');
|
||||
browserTrigger(link, 'click');
|
||||
expectRewriteTo($browser, 'http://host.com/base/index.html#!/partial1');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('should rewrite abs link to hashbang url when history enabled on old browser', function() {
|
||||
configureService('/base/link?a#b', true, false);
|
||||
inject(
|
||||
@@ -986,6 +1020,32 @@ describe('$location', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should not rewrite links with `javascript:` URI', function() {
|
||||
configureService(' jAvAsCrIpT:throw new Error("Boom!")', true, true, true);
|
||||
inject(
|
||||
initBrowser(),
|
||||
initLocation(),
|
||||
function($browser) {
|
||||
browserTrigger(link, 'click');
|
||||
expectNoRewrite($browser);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('should not rewrite links with `mailto:` URI', function() {
|
||||
configureService(' mAiLtO:foo@bar.com', true, true, true);
|
||||
inject(
|
||||
initBrowser(),
|
||||
initLocation(),
|
||||
function($browser) {
|
||||
browserTrigger(link, 'click');
|
||||
expectNoRewrite($browser);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
it('should rewrite full url links to same domain and base path', function() {
|
||||
configureService('http://host.com/base/new', true);
|
||||
inject(
|
||||
|
||||
@@ -434,6 +434,17 @@ describe('parser', function() {
|
||||
expect(scope.b).toEqual(234);
|
||||
});
|
||||
|
||||
it('should evaluate assignments in ternary operator', function() {
|
||||
scope.$eval('a = 1 ? 2 : 3');
|
||||
expect(scope.a).toBe(2);
|
||||
|
||||
scope.$eval('0 ? a = 2 : a = 3');
|
||||
expect(scope.a).toBe(3);
|
||||
|
||||
scope.$eval('1 ? a = 2 : a = 3');
|
||||
expect(scope.a).toBe(2);
|
||||
});
|
||||
|
||||
it('should evaluate function call without arguments', function() {
|
||||
scope['const'] = function(a,b){return 123;};
|
||||
expect(scope.$eval("const()")).toEqual(123);
|
||||
@@ -1089,6 +1100,22 @@ describe('parser', function() {
|
||||
fn.assign(scope, 123);
|
||||
expect(scope).toEqual({a:123});
|
||||
}));
|
||||
|
||||
it('should expose working assignment function for expressions ending with brackets', inject(function($parse) {
|
||||
var fn = $parse('a.b["c"]');
|
||||
expect(fn.assign).toBeTruthy();
|
||||
var scope = {};
|
||||
fn.assign(scope, 123);
|
||||
expect(scope.a.b.c).toEqual(123);
|
||||
}));
|
||||
|
||||
it('should expose working assignment function for expressions with brackets in the middle', inject(function($parse) {
|
||||
var fn = $parse('a["b"].c');
|
||||
expect(fn.assign).toBeTruthy();
|
||||
var scope = {};
|
||||
fn.assign(scope, 123);
|
||||
expect(scope.a.b.c).toEqual(123);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,12 @@ describe('sanitizeUri', function() {
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
|
||||
});
|
||||
|
||||
it('should sanitize javascript: urls with comments', function() {
|
||||
/* jshint scripturl:true */
|
||||
testUrl = "javascript:alert(1)//data:image/";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:alert(1)//data:image/');
|
||||
});
|
||||
|
||||
it('should sanitize non-image data: urls', function() {
|
||||
testUrl = "data:application/javascript;charset=US-ASCII,alert('evil!');";
|
||||
expect(sanitizeImg(testUrl)).toBe("unsafe:data:application/javascript;charset=US-ASCII,alert('evil!');");
|
||||
@@ -235,4 +241,4 @@ describe('sanitizeUri', function() {
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -25,6 +25,8 @@ describe('linky', function() {
|
||||
toEqual('<a href="mailto:me@example.com">me@example.com</a>');
|
||||
expect(linky("send email to me@example.com, but")).
|
||||
toEqual('send email to <a href="mailto:me@example.com">me@example.com</a>, but');
|
||||
expect(linky("my email is \"me@example.com\"")).
|
||||
toEqual('my email is "<a href="mailto:me@example.com">me@example.com</a>"');
|
||||
});
|
||||
|
||||
it('should handle target:', function() {
|
||||
|
||||
@@ -228,6 +228,16 @@ describe('HTML', function() {
|
||||
.toEqual('<p> 10 < <span>100</span> </p>');
|
||||
});
|
||||
|
||||
it('should accept non-string arguments', function() {
|
||||
expectHTML(null).toBe('');
|
||||
expectHTML(undefined).toBe('');
|
||||
expectHTML(42).toBe('42');
|
||||
expectHTML({}).toBe('[object Object]');
|
||||
expectHTML([1, 2, 3]).toBe('1,2,3');
|
||||
expectHTML(true).toBe('true');
|
||||
expectHTML(false).toBe('false');
|
||||
});
|
||||
|
||||
describe('htmlSanitizerWriter', function() {
|
||||
/* global htmlSanitizeWriter: false */
|
||||
if (angular.isUndefined(window.htmlSanitizeWriter)) return;
|
||||
|
||||
Reference in New Issue
Block a user