Compare commits
129 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22b669407c | |||
| 99a063f9cb | |||
| 27db9f018e | |||
| 2ed2399d49 | |||
| 9d2de344f5 | |||
| 9c0659ca27 | |||
| 3b9963c54a | |||
| 26db937377 | |||
| 1d0e582b3f | |||
| a6b58019e1 | |||
| 38fd896b74 | |||
| f96d553822 | |||
| f4149b70bc | |||
| 8a8c94c3d5 | |||
| d9de526059 | |||
| 0318761f7a | |||
| 74f7b57823 | |||
| 995ccfc9f6 | |||
| 72c244564a | |||
| 4bfe7e8663 | |||
| 63fe144397 | |||
| 0f551e6109 | |||
| 541f4ba21f | |||
| 6e7d04ddb2 | |||
| 06fb391687 | |||
| 203dd4d1e3 | |||
| c991f93817 | |||
| 52ddc13bee | |||
| 1645924d49 | |||
| 6bee655679 | |||
| acd455181d | |||
| a89b6e27a9 | |||
| 46d559dc8d | |||
| 26212fb204 | |||
| 244cfc7cd5 | |||
| c6074dc34c | |||
| 4930ef2bfe | |||
| 1778d347cd | |||
| 4724d56c93 | |||
| 70fcffc375 | |||
| 181e44019e | |||
| 5ce859bcdd | |||
| fb994e4d90 | |||
| 55f26fc246 | |||
| 1161002857 | |||
| 7ccfe92bed | |||
| a126fcfee3 | |||
| f22af2b874 | |||
| 2ca4170903 | |||
| fd57df1235 | |||
| 3010ed4ee5 | |||
| de59ca7107 | |||
| fa3214cfb8 | |||
| a8cacfe938 | |||
| 1a387ba5dd | |||
| 98e4a220fe | |||
| 9001ae2822 | |||
| c405f88bbc | |||
| 159a68ec7b | |||
| b9a56d588f | |||
| f797f83cd6 | |||
| 6df970eb3c | |||
| f88b4be92f | |||
| a7bcc01581 | |||
| 40ed8094d1 | |||
| 1a722f7b19 | |||
| 4991207c7c | |||
| 33514ec384 | |||
| 28bd20ed30 | |||
| c3bfd7f59d | |||
| 553cccdb85 | |||
| c7dd2c24f4 | |||
| fbf5aebda2 | |||
| ab35c16ccf | |||
| 37b6a71678 | |||
| e79ebff223 | |||
| 36b38e1355 | |||
| 3168d95793 | |||
| 538c63f057 | |||
| a9a1461351 | |||
| 29190f9318 | |||
| ca7d2b3b3f | |||
| 81676f8323 | |||
| 6f9d07d9c7 | |||
| 5201b8146a | |||
| 6aa83d04b7 | |||
| cfbe1a701a | |||
| aa38ded59d | |||
| b03957f004 | |||
| 56f33af890 | |||
| 41bea59eb7 | |||
| 6d0dcca6f1 | |||
| f423dc6b51 | |||
| 9fca87ca5b | |||
| ebbaa4ac5e | |||
| 617137231e | |||
| 71b966ace7 | |||
| 488d5b4288 | |||
| 01b8772cca | |||
| 05ac0ba926 | |||
| 399e872228 | |||
| 3115408be7 | |||
| aea39e9fdc | |||
| 51f6752cec | |||
| db58801a55 | |||
| 2e33d170be | |||
| cc6dcb4bc2 | |||
| 294d6793fd | |||
| cefcc46e1b | |||
| c10dba144e | |||
| f05ffd9bf1 | |||
| e208d81293 | |||
| b51fca1a2b | |||
| 6685043ad4 | |||
| f176a2dbb7 | |||
| 3a366bd42b | |||
| 1c5db02638 | |||
| ffd5308d86 | |||
| aed9de9d37 | |||
| aab6825e85 | |||
| c4fad6ad61 | |||
| 2a7c37cf01 | |||
| 194c3adfed | |||
| 371957d9d5 | |||
| 1e5d36d4b0 | |||
| 3749c85829 | |||
| 886b59d317 | |||
| 2048b6df24 | |||
| 5ec0a50d39 |
@@ -1,3 +1,258 @@
|
||||
<a name="1.5.8"></a>
|
||||
# 1.5.8 arbitrary-fallbacks (2016-07-22)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$animate:** do not get affected by custom, enumerable properties on `Object.prototype`
|
||||
([181e4401](https://github.com/angular/angular.js/commit/181e44019e850e5253378e29415cddf8d768bbef),
|
||||
[#14804](https://github.com/angular/angular.js/issues/14804), [#14830](https://github.com/angular/angular.js/issues/14830))
|
||||
- **$compile:** ensure `$doCheck` hooks can be defined in the controller constructor
|
||||
([3010ed4e](https://github.com/angular/angular.js/commit/3010ed4ee5c2c18b9848b5664639971b9fdc8919),
|
||||
[#14811](https://github.com/angular/angular.js/issues/14811))
|
||||
- **$injector:** fix class detection RegExp
|
||||
([4724d56c](https://github.com/angular/angular.js/commit/4724d56c939d02675433e7fe554608dff97ecf81),
|
||||
[#14533](https://github.com/angular/angular.js/issues/14533))
|
||||
- **$jsonpCallbacks:** do not overwrite callbacks added by other apps
|
||||
([1778d347](https://github.com/angular/angular.js/commit/1778d347cd3c91335e3840eaa49f1e387db8602f),
|
||||
[#14824](https://github.com/angular/angular.js/issues/14824))
|
||||
- **$timeout:** make $flush handle new $timeouts added in $timeout callbacks
|
||||
([1a387ba5](https://github.com/angular/angular.js/commit/1a387ba5dd1a8a831486fce23f6795bd1eef3d8b),
|
||||
[#5420](https://github.com/angular/angular.js/issues/5420), [#14686](https://github.com/angular/angular.js/issues/14686))
|
||||
- **copy:** fix handling of typed subarrays
|
||||
([1645924d](https://github.com/angular/angular.js/commit/1645924d49a7905ce55cced4c4276654970bb226),
|
||||
[#14842](https://github.com/angular/angular.js/issues/14842), [#14845](https://github.com/angular/angular.js/issues/14845))
|
||||
- **modules:** allow modules to be loaded in any order when using `angular-loader`
|
||||
([98e4a220](https://github.com/angular/angular.js/commit/98e4a220fe8301cec35498ae592adc0266f12437),
|
||||
[#9140](https://github.com/angular/angular.js/issues/9140), [#14794](https://github.com/angular/angular.js/issues/14794))
|
||||
- **ngAnimate:** allow removal of class that is scheduled to be added with requestAnimationFrame
|
||||
([7ccfe92b](https://github.com/angular/angular.js/commit/7ccfe92bed7361832f1b8d25b1a8411eb24d3fb5),
|
||||
[#14582](https://github.com/angular/angular.js/issues/14582))
|
||||
- **ngMocks:** allow `ErrorAddingDeclarationLocationStack` to be recognized as an `Error`
|
||||
([c6074dc3](https://github.com/angular/angular.js/commit/c6074dc34c31a07269bf7f628b971ef6dc805f17),
|
||||
[#13821](https://github.com/angular/angular.js/issues/13821), [#14344](https://github.com/angular/angular.js/issues/14344))
|
||||
- **ngOptions:** don't duplicate groups with falsy values
|
||||
([c3bfd7f5](https://github.com/angular/angular.js/commit/c3bfd7f59d0ecbf4ba3253fb407e683c7bb0766c))
|
||||
- **ngTransclude:**
|
||||
- ensure that fallback content is compiled and linked correctly
|
||||
([c405f88b](https://github.com/angular/angular.js/commit/c405f88bbc743f41591f6f3cfc022eea3c6c34ad),
|
||||
[#14787](https://github.com/angular/angular.js/issues/14787))
|
||||
- only compile fallback content if necessary
|
||||
([159a68ec](https://github.com/angular/angular.js/commit/159a68ec7ba77e9128b0d0516b813ed3d223b6e3),
|
||||
[#14768](https://github.com/angular/angular.js/issues/14768), [#14765](https://github.com/angular/angular.js/issues/14765), [#14775](https://github.com/angular/angular.js/issues/14775))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:** backport $doCheck
|
||||
([de59ca71](https://github.com/angular/angular.js/commit/de59ca71072eac95ee68de308f92bc5f921dd07b),
|
||||
[#14656](https://github.com/angular/angular.js/issues/14656))
|
||||
- **$jsonpCallbacks:** new service to abstract how JSONP callbacks are handled
|
||||
([a8cacfe9](https://github.com/angular/angular.js/commit/a8cacfe938287c54ce7099125cb735ad53f4c7c2),
|
||||
[#14795](https://github.com/angular/angular.js/issues/14795))
|
||||
- **$q:** implement $q.race
|
||||
([b9a56d58](https://github.com/angular/angular.js/commit/b9a56d588f8b597b1dff30d8e184b7c37d94cdcf),
|
||||
[#12929](https://github.com/angular/angular.js/issues/12929), [#14757](https://github.com/angular/angular.js/issues/14757))
|
||||
- **$resource:** pass the resource to a dynamic param functions
|
||||
([a126fcfe](https://github.com/angular/angular.js/commit/a126fcfee3bd8b02869bd2542c73e1eb21afe927),
|
||||
[#4899](https://github.com/angular/angular.js/issues/4899))
|
||||
- **$swipe:** add pointer support
|
||||
([f797f83c](https://github.com/angular/angular.js/commit/f797f83cd66f1fd11b3c9399e7894217ffa06c38),
|
||||
[#14061](https://github.com/angular/angular.js/issues/14061), [#14791](https://github.com/angular/angular.js/issues/14791))
|
||||
- **filterFilter:** allow overwriting the special `$` property name
|
||||
([33514ec3](https://github.com/angular/angular.js/commit/33514ec384d676d84b2a445bc15bae38c8c3ac8d),
|
||||
[#13313](https://github.com/angular/angular.js/issues/13313))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$compile:** wrap try/catch of collect comment directives into a function to avoid V8 deopt
|
||||
([acd45518](https://github.com/angular/angular.js/commit/acd455181de1cfa6b34d75f8d71a6c0b6995a777),
|
||||
[#14848](https://github.com/angular/angular.js/issues/14848))
|
||||
|
||||
|
||||
<a name="1.2.30"></a>
|
||||
# 1.2.30 patronal-resurrection (2016-07-21)
|
||||
|
||||
|
||||
_**Note:** This release contains some security fixes that required breaking changes. Since the
|
||||
legacy 1.2.x branch is the only version branch that supports IE8, it was necessary to introduce a
|
||||
couple of low-impact breaking changes in a patch release - something we generally avoid - in order
|
||||
to make the fixes available to people that still need IE8 support._
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- secure `link[href]` as a `RESOURCE_URL`s in `$sce`
|
||||
([f35f334b](https://github.com/angular/angular.js/commit/f35f334bd3197585bdf034f4b6d9ffa3122dac62),
|
||||
[#14687](https://github.com/angular/angular.js/issues/14687))
|
||||
- properly sanitize `xlink:href` attribute interoplation
|
||||
([f2fa1ed8](https://github.com/angular/angular.js/commit/f2fa1ed83d18d4e79a36f8c0db1c2524d762e513),
|
||||
[2687c261](https://github.com/angular/angular.js/commit/2687c26140585d9e3716f9f559390f5d8d598fdf))
|
||||
- **ngSanitize:** blacklist the attribute `usemap` as it can be used as a security exploit
|
||||
([ac0d5286](https://github.com/angular/angular.js/commit/ac0d5286b8931633d774080d6396fb4825d8be33),
|
||||
[#14903](https://github.com/angular/angular.js/issues/14903))
|
||||
- **ngAnimate:** do not use event.timeStamp anymore for time tracking
|
||||
([8d83b563](https://github.com/angular/angular.js/commit/8d83b5633471c847d58f337426fe069797dd49d9),
|
||||
[#13494](https://github.com/angular/angular.js/issues/13494), [#13495](https://github.com/angular/angular.js/issues/13495))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$compile:** due to [f35f334b](https://github.com/angular/angular.js/commit/f35f334bd3197585bdf034f4b6d9ffa3122dac62),
|
||||
|
||||
`link[href]` attributes are now protected via `$sce`, which prevents interpolated values that fail
|
||||
the `RESOURCE_URL` context tests from being used in interpolation. For example if the application is
|
||||
running at `https://mydomain.org/` then the following will fail:
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="{{ 'https://otherdomain.org/unsafe.css' }}" />
|
||||
```
|
||||
|
||||
By default, `RESOURCE_URL` safe URLs are only allowed from the same domain and protocol as the
|
||||
application document. To use URLs from other domains and/or protocols, you may either whitelist them
|
||||
using `$sceDelegateProvider.resourceUrlWhitelist(...)` or wrap them into a trusted value by calling
|
||||
`$sce.trustAsResourceUrl(url)`.
|
||||
|
||||
- **ngSanitize:** due to [234053fc](https://github.com/angular/angular.js/commit/234053fc9ad90e0d05be7e8359c6af66be94c094),
|
||||
|
||||
The `$sanitize` service will now remove instances of the `usemap` attribute from any elements passed
|
||||
to it.
|
||||
|
||||
This attribute is used to reference another element by `name` or `id`. Since the `name` and `id`
|
||||
attributes are already blacklisted, a sanitized `usemap` attribute could only reference unsanitized
|
||||
content, which is a security risk.
|
||||
|
||||
|
||||
<a name="1.5.7"></a>
|
||||
# 1.5.7 hexagonal-circumvolution (2016-06-15)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- don't add merged attributes twice to $attrs
|
||||
([ebbaa4ac](https://github.com/angular/angular.js/commit/ebbaa4ac5e3559df9267203438a0bb18c2d3b7d8),
|
||||
[#8159](https://github.com/angular/angular.js/issues/8159), [#14737](https://github.com/angular/angular.js/issues/14737))
|
||||
- cope with `$onChanges` hooks throwing
|
||||
([3749c858](https://github.com/angular/angular.js/commit/3749c85829406ca57cc5729e80696c7f34134068),
|
||||
[#14444](https://github.com/angular/angular.js/issues/14444), [#14463](https://github.com/angular/angular.js/issues/14463))
|
||||
- **$parse:** allow arguments to contain filter chains
|
||||
([cc6dcb4b](https://github.com/angular/angular.js/commit/cc6dcb4bc28aadff4f62d76d6451b0f80b928e69),
|
||||
[#4175](https://github.com/angular/angular.js/issues/4175), [#4168](https://github.com/angular/angular.js/issues/4168), [#14720](https://github.com/angular/angular.js/issues/14720))
|
||||
- **$routeProvider:** do not deep-copy route definition objects
|
||||
([6d0dcca6](https://github.com/angular/angular.js/commit/6d0dcca6f18a353a12c356dc96e05475d351b795),
|
||||
[#14478](https://github.com/angular/angular.js/issues/14478), [#14699](https://github.com/angular/angular.js/issues/14699), [#14750](https://github.com/angular/angular.js/issues/14750))
|
||||
- **input[email]:** improve email address validation
|
||||
([f423dc6b](https://github.com/angular/angular.js/commit/f423dc6b51a9b4a09728a287a2cabda7d45f192e),
|
||||
[#14719](https://github.com/angular/angular.js/issues/14719))
|
||||
- **ngMessages:** create new scope for ngMessage, clean it up correctly
|
||||
([56f33af8](https://github.com/angular/angular.js/commit/56f33af89045e2ec18d144d9d1ef73affbe51959),
|
||||
[#14307](https://github.com/angular/angular.js/issues/14307))
|
||||
- **ngMessagesInclude:** don't break on empty (or whitespace-only) templates
|
||||
([01b8772c](https://github.com/angular/angular.js/commit/01b8772cca55916376355a2ae58d3ab7832a4bc2),
|
||||
[#12941](https://github.com/angular/angular.js/issues/12941), [#14726](https://github.com/angular/angular.js/issues/14726))
|
||||
- **ngMock#$controller:** properly assign bindings to all types of controllers (e.g. class-based)
|
||||
([db58801a](https://github.com/angular/angular.js/commit/db58801a55c91df755414387dc00fee5902bb5f3),
|
||||
[#14437](https://github.com/angular/angular.js/issues/14437), [#14439](https://github.com/angular/angular.js/issues/14439))
|
||||
- **ngMockE2E:** allow $httpBackend.passThrough() to work when ngMock is loaded
|
||||
([6685043a](https://github.com/angular/angular.js/commit/6685043ad40acc50d7088f87e2a71f76d2265306),
|
||||
[#1434](https://github.com/angular/angular.js/issues/1434), [#13124](https://github.com/angular/angular.js/issues/13124))
|
||||
- **ngSanitize:** call attribute setter in linky for all links
|
||||
([c4fad6ad](https://github.com/angular/angular.js/commit/c4fad6ad617af025984ca401054f7b402aa28f1d),
|
||||
[#14707](https://github.com/angular/angular.js/issues/14707))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **limitTo:** add support for array-like objects
|
||||
([b03957f0](https://github.com/angular/angular.js/commit/b03957f0047fcfe996abc8efe97f9e2be2c2e13a),
|
||||
[#14657](https://github.com/angular/angular.js/issues/14657), [#14694](https://github.com/angular/angular.js/issues/14694))
|
||||
- **orderBy:** add support for custom comparators
|
||||
([2e33d170](https://github.com/angular/angular.js/commit/2e33d170be7eb72f40fd57a8f66631583572fbf2),
|
||||
[#13238](https://github.com/angular/angular.js/issues/13238), [#14455](https://github.com/angular/angular.js/issues/14455), [#5123](https://github.com/angular/angular.js/issues/5123), [#8112](https://github.com/angular/angular.js/issues/8112), [#10368](https://github.com/angular/angular.js/issues/10368), [#14468](https://github.com/angular/angular.js/issues/14468))
|
||||
|
||||
|
||||
<a name="1.5.6"></a>
|
||||
# 1.5.6 arrow-stringification (2016-05-27)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$browser:** set the url even if the browser transforms it
|
||||
([743b33ab](https://github.com/angular/angular.js/commit/743b33ab9d8cdb8aa6ff28028a54ac0023f4dc15),
|
||||
[#14427](https://github.com/angular/angular.js/issues/14427), [#14499](https://github.com/angular/angular.js/issues/14499))
|
||||
- **$compile:**
|
||||
- properly bind context to linking functions for directives with `templateUrl`
|
||||
([5ae72dc7](https://github.com/angular/angular.js/commit/5ae72dc7affb252774b7c4cbf278269c5e178f38))
|
||||
- always use the DDO as `this` in pre-/post-linking functions
|
||||
([47bc98a1](https://github.com/angular/angular.js/commit/47bc98a1eafd7853babc1a7dc54f13363a3f4a43),
|
||||
[#9306](https://github.com/angular/angular.js/issues/9306))
|
||||
- don't run unnecessary update to one-way bindings
|
||||
([30479647](https://github.com/angular/angular.js/commit/304796471292f9805b9cf77e51aacc9cfbb09921),
|
||||
[#14546](https://github.com/angular/angular.js/issues/14546), [#14580](https://github.com/angular/angular.js/issues/14580))
|
||||
- removing unnecessary white space in element-transclusion comments
|
||||
([1116a96d](https://github.com/angular/angular.js/commit/1116a96df662c95aa83ab527c479b644a14532b6),
|
||||
[#14549](https://github.com/angular/angular.js/issues/14549), [#14550](https://github.com/angular/angular.js/issues/14550))
|
||||
- properly handle setting `srcset` to undefined
|
||||
([308f22ba](https://github.com/angular/angular.js/commit/308f22ba9a803967ce84c995d810990c80478f6f),
|
||||
[#14470](https://github.com/angular/angular.js/issues/14470), [#14493](https://github.com/angular/angular.js/issues/14493))
|
||||
- **$injector:**
|
||||
- add workaround for class stringification in Chrome v50/51
|
||||
([7550b7bd](https://github.com/angular/angular.js/commit/7550b7bda8d992cc6ed3c65b1f9f7e323a09cae4),
|
||||
[#14531](https://github.com/angular/angular.js/issues/14531))
|
||||
- add workaround for fat-arrow stringification in Chrome v50/51
|
||||
([bb730704](https://github.com/angular/angular.js/commit/bb730704f2f3dea4620e7b40083dcd65f208e2b2),
|
||||
[#14487](https://github.com/angular/angular.js/issues/14487), [#14495](https://github.com/angular/angular.js/issues/14495))
|
||||
- **$templateRequest:** trust empty templates in `$templateCache` as well
|
||||
([10955969](https://github.com/angular/angular.js/commit/10955969620248bd31a0bd37bc9fd7cc3b1f5e88),
|
||||
[#14479](https://github.com/angular/angular.js/issues/14479), [#14496](https://github.com/angular/angular.js/issues/14496))
|
||||
- **filters:** always call `splice()` with 2 arguments or more
|
||||
([6f1fae8a](https://github.com/angular/angular.js/commit/6f1fae8a916cc6f26725f64869f86fcd4991e819),
|
||||
[#14467](https://github.com/angular/angular.js/issues/14467), [#14489](https://github.com/angular/angular.js/issues/14489))
|
||||
- **ng-bind-html:** watch the unwrapped value using `$sce.valueOf()` (instead of `toString()`)
|
||||
([1c1c9b27](https://github.com/angular/angular.js/commit/1c1c9b27cbb57b3219d4c9765eeea8a11553d297),
|
||||
[#14526](https://github.com/angular/angular.js/issues/14526), [#14527](https://github.com/angular/angular.js/issues/14527))
|
||||
- **ngAnimate:**
|
||||
- don't break on anchored animations without duration
|
||||
([21b76aed](https://github.com/angular/angular.js/commit/21b76aed06d9cc04bd25a99c23ba852af782b357),
|
||||
[#14641](https://github.com/angular/angular.js/issues/14641), [#14645](https://github.com/angular/angular.js/issues/14645))
|
||||
- properly handle empty jqLite collections
|
||||
([9f480257](https://github.com/angular/angular.js/commit/9f4802573348401ee24090f815f3138fa17c161d),
|
||||
[#14558](https://github.com/angular/angular.js/issues/14558), [#14559](https://github.com/angular/angular.js/issues/14559))
|
||||
- **ngMessages:** do not compile ngMessagesInclude template if scope is destroyed
|
||||
([69d9072d](https://github.com/angular/angular.js/commit/69d9072d5b5aba988ac8a9717c92ce1fac465cbe),
|
||||
[#12695](https://github.com/angular/angular.js/issues/12695), [#14640](https://github.com/angular/angular.js/issues/14640))
|
||||
- **ngMock:**
|
||||
- match HTTP request regardless of the order of query params
|
||||
([363fb16c](https://github.com/angular/angular.js/commit/363fb16c1076a806bb31006daaf583ab5c82e467),
|
||||
[#12762](https://github.com/angular/angular.js/issues/12762))
|
||||
- pass eventHandlers to $httpBackend if passThrough is active
|
||||
([147650b3](https://github.com/angular/angular.js/commit/147650b3beae7ad8b556a930129887d187a0f5d3),
|
||||
[#14471](https://github.com/angular/angular.js/issues/14471))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:** support omitting required controller name if same as the local name
|
||||
([8965d57a](https://github.com/angular/angular.js/commit/8965d57aa0788c96859cf3cfa0f78b46d2cebedb),
|
||||
[#14513](https://github.com/angular/angular.js/issues/14513))
|
||||
- **$parse:** Add support for ES6 object initializers
|
||||
([1c9b9e24](https://github.com/angular/angular.js/commit/1c9b9e24ede7efc10ce4d53c6ab5528cc77e79d7))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$rootScope:** make queues more efficient
|
||||
([51b78f61](https://github.com/angular/angular.js/commit/51b78f61f954d94dc937f57190c9f881df9ab3ad),
|
||||
[#14545](https://github.com/angular/angular.js/issues/14545))
|
||||
- **ngAnimate:** listen for document visibility changes
|
||||
([b377d6b0](https://github.com/angular/angular.js/commit/b377d6b043db9c8da4e68493892f75a0f9759464),
|
||||
[#14568](https://github.com/angular/angular.js/issues/14568))
|
||||
- **ngClass:** improve even-odd checking
|
||||
([cf1b1755](https://github.com/angular/angular.js/commit/cf1b175508a72d4b7e3d64b8506903096c6ec25f))
|
||||
|
||||
|
||||
<a name="1.5.5"></a>
|
||||
# 1.5.5 material-conspiration (2016-04-18)
|
||||
|
||||
@@ -432,7 +687,8 @@ changes section for more information
|
||||
|
||||
- **ngSanitize:** due to [234053fc](https://github.com/angular/angular.js/commit/234053fc9ad90e0d05be7e8359c6af66be94c094),
|
||||
|
||||
The `$sanitize` service will now remove instances of the `usemap` attribute from any elements passed to it.
|
||||
The `$sanitize` service will now remove instances of the `usemap` attribute from any elements passed
|
||||
to it.
|
||||
|
||||
This attribute is used to reference another element by `name` or `id`. Since the `name` and `id`
|
||||
attributes are already blacklisted, a sanitized `usemap` attribute could only reference unsanitized
|
||||
@@ -1363,6 +1619,17 @@ requirement more strict and alerts the developer explicitly.
|
||||
[#9936](https://github.com/angular/angular.js/issues/9936))
|
||||
|
||||
|
||||
<a name="1.5.0-beta.0"></a>
|
||||
|
||||
# 1.5.0-beta.0 intialization-processation (2015-09-17)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **jqLite:**: properly handle dash-delimited node names in `jqLiteBuildFragment`
|
||||
([cdd1227a3](https://github.com/angular/angular.js/commit/cdd1227a308edd34d31b67f338083b6e0c4c0db9),
|
||||
[#10617](https://github.com/angular/angular.js/issues/10617))
|
||||
|
||||
|
||||
<a name="1.4.6"></a>
|
||||
# 1.4.6 multiplicative-elevation (2015-09-17)
|
||||
|
||||
@@ -1913,6 +2180,44 @@ describe('$q.when', function() {
|
||||
[#11580](https://github.com/angular/angular.js/issues/11580), [#12234](https://github.com/angular/angular.js/issues/12234))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **ngInclude:** due to [3c6e8ce044446735eb2e70d0061db8c6db050289](https://github.com/angular/angular.js/commit/3c6e8ce044446735eb2e70d0061db8c6db050289), the `src` attribute of ngInclude no longer accepts an
|
||||
expression that returns the result of `$sce.trustAsResourceUrl`. This will now cause an infinite digest:
|
||||
|
||||
Before:
|
||||
```html
|
||||
<div ng-include="findTemplate('https://example.com/myTemplate.html')"></div>
|
||||
```
|
||||
|
||||
```js
|
||||
$scope.findTemplate = function(templateName) {
|
||||
return $sce.trustAsResourceUrl(templateName);
|
||||
};
|
||||
```
|
||||
|
||||
To migrate, either cache the result of `trustAsResourceUrl()`, or put the template url in the resource
|
||||
whitelist in the `config()` function:
|
||||
|
||||
After:
|
||||
|
||||
```js
|
||||
var templateCache = {};
|
||||
$scope.findTemplate = function(templateName) {
|
||||
if (!templateCache[templateName]) {
|
||||
templateCache[templateName] = $sce.trustAsResourceUrl(templateName);
|
||||
}
|
||||
|
||||
return templateCache[templateName];
|
||||
};
|
||||
|
||||
// Alternatively, use `$sceDelegateProvider.resourceUrlWhitelist()`:
|
||||
|
||||
angular.module('myApp', []).config(function($sceDelegateProvider) {
|
||||
$sceDelegateProvider.resourceUrlWhitelist(['self', 'https://example.com/**'])
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
<a name="1.3.17"></a>
|
||||
# 1.3.17 tsktskskly-euouae (2015-07-06)
|
||||
|
||||
@@ -17,6 +17,10 @@ module.exports = function(grunt) {
|
||||
NG_VERSION.cdn = versionInfo.cdnVersion;
|
||||
var dist = 'angular-'+ NG_VERSION.full;
|
||||
|
||||
if (versionInfo.cdnVersion == null) {
|
||||
throw new Error('Unable to read CDN version, are you offline or has the CDN not been properly pushed?');
|
||||
}
|
||||
|
||||
//config
|
||||
grunt.initConfig({
|
||||
NG_VERSION: NG_VERSION,
|
||||
@@ -79,6 +83,7 @@ module.exports = function(grunt) {
|
||||
tests: {
|
||||
jqlite: 'karma-jqlite.conf.js',
|
||||
jquery: 'karma-jquery.conf.js',
|
||||
'jquery-2.2': 'karma-jquery-2.2.conf.js',
|
||||
'jquery-2.1': 'karma-jquery-2.1.conf.js',
|
||||
docs: 'karma-docs.conf.js',
|
||||
modules: 'karma-modules.conf.js'
|
||||
@@ -88,6 +93,7 @@ module.exports = function(grunt) {
|
||||
autotest: {
|
||||
jqlite: 'karma-jqlite.conf.js',
|
||||
jquery: 'karma-jquery.conf.js',
|
||||
'jquery-2.2': 'karma-jquery-2.2.conf.js',
|
||||
'jquery-2.1': 'karma-jquery-2.1.conf.js',
|
||||
modules: 'karma-modules.conf.js',
|
||||
docs: 'karma-docs.conf.js'
|
||||
@@ -360,10 +366,11 @@ module.exports = function(grunt) {
|
||||
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'jscs', 'package', 'test:unit', 'test:promises-aplus', 'tests:docs', 'test:protractor']);
|
||||
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
|
||||
grunt.registerTask('test:jquery', 'Run the jQuery (latest) unit tests with Karma', ['tests:jquery']);
|
||||
grunt.registerTask('test:jquery-2.2', 'Run the jQuery 2.2 unit tests with Karma', ['tests:jquery-2.2']);
|
||||
grunt.registerTask('test:jquery-2.1', 'Run the jQuery 2.1 unit tests with Karma', ['tests:jquery-2.1']);
|
||||
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['build', 'tests:modules']);
|
||||
grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']);
|
||||
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['test:jqlite', 'test:jquery', 'test:jquery-2.1', 'test:modules']);
|
||||
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['test:jqlite', 'test:jquery', 'test:jquery-2.2', 'test:jquery-2.1', 'test:modules']);
|
||||
grunt.registerTask('test:protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'protractor:normal']);
|
||||
grunt.registerTask('test:travis-protractor', 'Run the end to end tests with Protractor for Travis CI builds', ['connect:testserver', 'protractor:travis']);
|
||||
grunt.registerTask('test:ci-protractor', 'Run the end to end tests with Protractor for Jenkins CI builds', ['webdriver', 'connect:testserver', 'protractor:jenkins']);
|
||||
|
||||
@@ -5,6 +5,7 @@ var angularFiles = {
|
||||
'src/minErr.js',
|
||||
'src/Angular.js',
|
||||
'src/loader.js',
|
||||
'src/shallowCopy.js',
|
||||
'src/stringify.js',
|
||||
'src/AngularPublic.js',
|
||||
'src/jqLite.js',
|
||||
@@ -27,6 +28,7 @@ var angularFiles = {
|
||||
'src/ng/httpBackend.js',
|
||||
'src/ng/interpolate.js',
|
||||
'src/ng/interval.js',
|
||||
'src/ng/jsonpCallbacks.js',
|
||||
'src/ng/locale.js',
|
||||
'src/ng/location.js',
|
||||
'src/ng/log.js',
|
||||
@@ -128,6 +130,7 @@ var angularFiles = {
|
||||
'src/ngResource/resource.js'
|
||||
],
|
||||
'ngRoute': [
|
||||
'src/shallowCopy.js',
|
||||
'src/ngRoute/route.js',
|
||||
'src/ngRoute/routeParams.js',
|
||||
'src/ngRoute/directive/ngView.js'
|
||||
@@ -175,6 +178,7 @@ var angularFiles = {
|
||||
'test/auto/*.js',
|
||||
'test/ng/**/*.js',
|
||||
'test/ngAnimate/*.js',
|
||||
'test/ngMessageFormat/*.js',
|
||||
'test/ngMessages/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngResource/*.js',
|
||||
@@ -212,7 +216,9 @@ var angularFiles = {
|
||||
'test/modules/no_bootstrap.js',
|
||||
'src/ngScenario/browserTrigger.js',
|
||||
'test/helpers/*.js',
|
||||
'test/ngAnimate/*.js',
|
||||
'test/ngMessageFormat/*.js',
|
||||
'test/ngMessages/*.js',
|
||||
'test/ngMock/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngRoute/**/*.js',
|
||||
@@ -231,15 +237,6 @@ var angularFiles = {
|
||||
'@angularTest'
|
||||
],
|
||||
|
||||
'karmaJqueryOld': [
|
||||
'bower_components/jquery-2.1/dist/jquery.js',
|
||||
'test/jquery_alias.js',
|
||||
'@angularSrc',
|
||||
'@angularSrcModules',
|
||||
'@angularScenario',
|
||||
'@angularTest'
|
||||
],
|
||||
|
||||
'karmaJqueryExclude': [
|
||||
'src/angular-bootstrap.js',
|
||||
'src/ngScenario/angular-bootstrap.js',
|
||||
@@ -248,6 +245,17 @@ var angularFiles = {
|
||||
]
|
||||
};
|
||||
|
||||
['2.1', '2.2'].forEach(function (jQueryVersion) {
|
||||
angularFiles['karmaJquery' + jQueryVersion] = []
|
||||
.concat(angularFiles.karmaJquery)
|
||||
.map(function (path) {
|
||||
if (path.startsWith('bower_components/jquery')) {
|
||||
return path.replace(/^bower_components\/jquery/, 'bower_components/jquery-' + jQueryVersion);
|
||||
}
|
||||
return path;
|
||||
});
|
||||
});
|
||||
|
||||
angularFiles['angularSrcModules'] = [].concat(
|
||||
angularFiles['angularModules']['ngAnimate'],
|
||||
angularFiles['angularModules']['ngMessageFormat'],
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"name": "AngularJS",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"jquery": "2.2.3",
|
||||
"jquery": "3.1.0",
|
||||
"jquery-2.2": "jquery#2.2.4",
|
||||
"jquery-2.1": "jquery#2.1.4",
|
||||
"closure-compiler": "https://dl.google.com/closure-compiler/compiler-20140814.zip",
|
||||
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip"
|
||||
|
||||
@@ -148,8 +148,6 @@ var writeChangelog = function(stream, commits, version) {
|
||||
breaks: {}
|
||||
};
|
||||
|
||||
sections.breaks[EMPTY_COMPONENT] = [];
|
||||
|
||||
commits.forEach(function(commit) {
|
||||
var section = sections[commit.type];
|
||||
var component = commit.component || EMPTY_COMPONENT;
|
||||
|
||||
@@ -39,7 +39,7 @@ describe('docs.angularjs.org', function () {
|
||||
|
||||
|
||||
it('should change the page content when clicking a link to a service', function () {
|
||||
browser.get('build/docs/index.html');
|
||||
browser.get('build/docs/index-production.html');
|
||||
|
||||
var ngBindLink = element(by.css('.definition-table td a[href="api/ng/directive/ngClick"]'));
|
||||
ngBindLink.click();
|
||||
@@ -51,33 +51,33 @@ describe('docs.angularjs.org', function () {
|
||||
|
||||
|
||||
it('should be resilient to trailing slashes', function() {
|
||||
browser.get('build/docs/index.html#!/api/ng/function/angular.noop/');
|
||||
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/');
|
||||
var pageBody = element(by.css('h1'));
|
||||
expect(pageBody.getText()).toEqual('angular.noop');
|
||||
});
|
||||
|
||||
|
||||
it('should be resilient to trailing "index"', function() {
|
||||
browser.get('build/docs/index.html#!/api/ng/function/angular.noop/index');
|
||||
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/index');
|
||||
var pageBody = element(by.css('h1'));
|
||||
expect(pageBody.getText()).toEqual('angular.noop');
|
||||
});
|
||||
|
||||
|
||||
it('should be resilient to trailing "index/"', function() {
|
||||
browser.get('build/docs/index.html#!/api/ng/function/angular.noop/index/');
|
||||
browser.get('build/docs/index-production.html#!/api/ng/function/angular.noop/index/');
|
||||
var pageBody = element(by.css('h1'));
|
||||
expect(pageBody.getText()).toEqual('angular.noop');
|
||||
});
|
||||
|
||||
|
||||
it('should display formatted error messages on error doc pages', function() {
|
||||
browser.get('build/docs/index.html#!error/ng/areq?p0=Missing&p1=not%20a%20function,%20got%20undefined');
|
||||
browser.get('build/docs/index-production.html#!error/ng/areq?p0=Missing&p1=not%20a%20function,%20got%20undefined');
|
||||
expect(element(by.css('.minerr-errmsg')).getText()).toEqual("Argument 'Missing' is not a function, got undefined");
|
||||
});
|
||||
|
||||
it("should display an error if the page does not exist", function() {
|
||||
browser.get('build/docs/index.html#!/api/does/not/exist');
|
||||
browser.get('build/docs/index-production.html#!/api/does/not/exist');
|
||||
expect(element(by.css('h1')).getText()).toBe('Oops!');
|
||||
});
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
</li>
|
||||
<li>
|
||||
<a href="http://bower.io">Bower</a> e.g.
|
||||
{% code %}bower install {$ doc.packageName $}@X.Y.Z{% endcode %}
|
||||
{% code %}bower install {$ doc.packageName $}#X.Y.Z{% endcode %}
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://code.angularjs.org/">code.angularjs.org</a>
|
||||
@@ -69,6 +69,7 @@
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if doc.componentGroups.length %}
|
||||
<div class="component-breakdown">
|
||||
<h2>Module Components</h2>
|
||||
{% for componentGroup in doc.componentGroups %}
|
||||
@@ -89,6 +90,7 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if doc.usage %}
|
||||
<h2>Usage</h2>
|
||||
|
||||
@@ -8,6 +8,8 @@ Welcome to the AngularJS API docs page. These pages contain the AngularJS refere
|
||||
The documentation is organized into **{@link guide/module modules}** which contain various components of an AngularJS application.
|
||||
These components are {@link guide/directive directives}, {@link guide/services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates templates}, global APIs, and testing mocks.
|
||||
|
||||
There is also a {@link guide/index guide} with articles on various topics, and a list of external resources.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Angular Prefixes `$` and `$$`**:
|
||||
|
||||
|
||||
@@ -313,8 +313,8 @@ This function can be used to enable / disable animations in two different ways:
|
||||
With a single `boolean` argument, it enables / disables animations globally: `$animate.enabled(false)`
|
||||
disables all animations in your app.
|
||||
|
||||
When the second argument is a native DOM or jQuery element, the function enables / disables
|
||||
animations on this element *and all its children*: `$animate.enabled(false, myElement)`. This is the
|
||||
When the first argument is a native DOM or jqLite/jQuery element, the function enables / disables
|
||||
animations on this element *and all its children*: `$animate.enabled(myElement, false)`. This is the
|
||||
most flexible way to change the animation state. For example, even if you have used it to disable
|
||||
animations on a parent element, you can still re-enable it for a child element. And compared to the
|
||||
`classNameFilter`, you can change the animation status at runtime instead of during the config phase.
|
||||
@@ -347,6 +347,7 @@ By setting `transition: 0s`, ngAnimate will ignore the existing transition style
|
||||
animations will still execute, though). This can be used to prevent {@link guide/animations#preventing-collisions-with-existing-animations-and-third-party-libraries
|
||||
issues with existing animations interfering with ngAnimate}.
|
||||
|
||||
|
||||
## Preventing flicker before an animation starts
|
||||
|
||||
When nesting elements with structural animations such as `ngIf` into elements that have class-based
|
||||
@@ -421,8 +422,25 @@ than any styles that are independent from ngAnimate.
|
||||
|
||||
You can also use one of the two other {@link guide/animations#how-to-selectively-enable-disable-and-skip-animations strategies to disable animations}.
|
||||
|
||||
|
||||
### Enable animations for elements outside of the Angular application DOM tree: {@link ng.$animate#pin $animate.pin()}
|
||||
|
||||
Before animating, `ngAnimate` checks to see if the element being animated is inside the application DOM tree,
|
||||
and if it is not, no animation is run. Usually, this is not a problem as most apps use the `ngApp`
|
||||
attribute / bootstrap the app on the `html` or `body` element.
|
||||
|
||||
Problems arise when the application is bootstrapped on a different element, and animations are
|
||||
attempted on elements that are outside the application tree, e.g. when libraries append popup and modal
|
||||
elements as the last child in the body tag.
|
||||
|
||||
You can use {@link ng.$animate#pin `$animate.pin(elementToAnimate, parentHost)`} to specify that an
|
||||
element belongs to your application. Simply call it before the element is added to the DOM / before
|
||||
the animation starts, with the element you want to animate, and the element which should be its
|
||||
assumed parent.
|
||||
|
||||
|
||||
## More about animations
|
||||
|
||||
For a full breakdown of each method available on `$animate`, see the {@link ng.$animate API documentation}.
|
||||
|
||||
To see a complete demo, see the {@link tutorial/step_12 animation step within the AngularJS phonecat tutorial}.
|
||||
To see a complete demo, see the {@link tutorial/step_14 animation step within the AngularJS phonecat tutorial}.
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
|
||||
# Component Router
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Deprecation Notice:** In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module) has been deprecated and will not receive further updates.
|
||||
We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
</div>
|
||||
|
||||
This guide describes the new Component Router for AngularJS 1.5.
|
||||
|
||||
<div class="alert alert-info">
|
||||
@@ -482,6 +487,14 @@ Just like any Angular application, we load the JavaScript files into our `index.
|
||||
<script src="/app/app.js"></script>
|
||||
```
|
||||
|
||||
You also need to include ES6 shims for browsers that do not support ES6 code (Internet Explorer,
|
||||
iOs < 8, Android < 5.0, Windows Mobile < 10):
|
||||
```html
|
||||
<!-- IE required polyfills, in this exact order -->
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.20/system-polyfills.js"></script>
|
||||
<script src="https://npmcdn.com/angular2/es6/dev/src/testing/shims_for_IE.js"></script>
|
||||
```
|
||||
|
||||
## Create the `app` module
|
||||
|
||||
|
||||
@@ -236,6 +236,10 @@ it upwards to the heroList component, which updates the original data.
|
||||
function HeroDetailController() {
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.delete = function() {
|
||||
ctrl.onDelete({hero: ctrl.hero});
|
||||
};
|
||||
|
||||
ctrl.update = function(prop, value) {
|
||||
ctrl.onUpdate({hero: ctrl.hero, prop: prop, value: value});
|
||||
};
|
||||
@@ -303,7 +307,7 @@ it upwards to the heroList component, which updates the original data.
|
||||
<div>
|
||||
Name: {{$ctrl.hero.name}}<br>
|
||||
Location: <editable-field field-value="$ctrl.hero.location" field-type="text" on-update="$ctrl.update('location', value)"></editable-field><br>
|
||||
<button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
|
||||
<button ng-click="$ctrl.delete()">Delete</button>
|
||||
</div>
|
||||
</file>
|
||||
<file name="editableField.html">
|
||||
@@ -443,44 +447,52 @@ angular.module('docsTabsExample', [])
|
||||
|
||||
# Unit-testing Component Controllers
|
||||
|
||||
The easiest way to unit-test a component controller is by using the {@link ngMock.$componentController $componentController}
|
||||
that is included in {@link ngMock}. The advantage of this method is that you do not have
|
||||
to create any DOM elements. The following example shows how to do this for the `heroDetail` component
|
||||
from above.
|
||||
The easiest way to unit-test a component controller is by using the
|
||||
{@link ngMock.$componentController $componentController} that is included in {@link ngMock}. The
|
||||
advantage of this method is that you do not have to create any DOM elements. The following example
|
||||
shows how to do this for the `heroDetail` component from above.
|
||||
|
||||
The examples use the [Jasmine](http://jasmine.github.io/) testing framework.
|
||||
|
||||
**Controller Test:**
|
||||
```js
|
||||
describe('component: heroDetail', function() {
|
||||
var component, scope, hero, $componentController;
|
||||
var $componentController;
|
||||
|
||||
beforeEach(module('heroApp'));
|
||||
|
||||
beforeEach(inject(function($rootScope, _$componentController_) {
|
||||
scope = $rootScope.$new();
|
||||
beforeEach(inject(function(_$componentController_) {
|
||||
$componentController = _$componentController_;
|
||||
hero = {name: 'Wolverine'};
|
||||
}));
|
||||
|
||||
it('should assign the name bindings to the hero object', function() {
|
||||
it('should expose a `hero` object', function() {
|
||||
// Here we are passing actual bindings to the component
|
||||
component = $componentController('heroDetail',
|
||||
null,
|
||||
{hero: hero}
|
||||
);
|
||||
expect(component.hero.name).toBe('Wolverine');
|
||||
var bindings = {hero: {name: 'Wolverine'}};
|
||||
var ctrl = $componentController('heroDetail', null, bindings);
|
||||
|
||||
expect(ctrl.hero).toBeDefined();
|
||||
expect(ctrl.hero.name).toBe('Wolverine');
|
||||
});
|
||||
|
||||
it('should call the onDelete binding when a hero is deleted', function() {
|
||||
var deleteSpy = jasmine.createSpy('deleteSpy');
|
||||
component = $componentController('heroDetail',
|
||||
null,
|
||||
{hero: hero, onDelete: deleteSpy}
|
||||
);
|
||||
it('should call the `onDelete` binding, when deleting the hero', function() {
|
||||
var onDeleteSpy = jasmine.createSpy('onDelete');
|
||||
var bindings = {hero: {}, onDelete: onDeleteSpy};
|
||||
var ctrl = $componentController('heroDetail', null, bindings);
|
||||
|
||||
component.onDelete({hero: component.hero});
|
||||
expect(deleteSpy).toHaveBeenCalledWith({hero: component.hero});
|
||||
ctrl.delete();
|
||||
expect(onDeleteSpy).toHaveBeenCalledWith({hero: ctrl.hero});
|
||||
});
|
||||
|
||||
it('should call the `onUpdate` binding, when updating a property', function() {
|
||||
var onUpdateSpy = jasmine.createSpy('onUpdate');
|
||||
var bindings = {hero: {}, onUpdate: onUpdateSpy};
|
||||
var ctrl = $componentController('heroDetail', null, bindings);
|
||||
|
||||
ctrl.update('foo', 'bar');
|
||||
expect(onUpdateSpy).toHaveBeenCalledWith({
|
||||
hero: ctrl.hero,
|
||||
prop: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -162,10 +162,6 @@ initialization work here. The function is invoked using
|
||||
{@link auto.$injector#invoke $injector.invoke} which makes it injectable just like a
|
||||
controller.
|
||||
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:** Prefer using the definition object over returning a function.
|
||||
</div>
|
||||
|
||||
|
||||
We'll go over a few common examples of directives, then dive deep into the different options
|
||||
and compilation process.
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
@ngdoc overview
|
||||
@name External Resources
|
||||
@sortOrder 205
|
||||
@description
|
||||
|
||||
# External Angular 1 Resources
|
||||
|
||||
This is a collection of external, 3rd party resources for learning and developing Angular.
|
||||
|
||||
## Articles, Videos, and Projects
|
||||
|
||||
### Introductory Material
|
||||
|
||||
* [10 Reasons Why You Should Use AngularJS](http://www.sitepoint.com/10-reasons-use-angularjs/)
|
||||
* [10 Reasons Why Developers Should Learn AngularJS](http://wintellect.com/blogs/jlikness/10-reasons-web-developers-should-learn-angularjs)
|
||||
* [Design Principles of AngularJS (video)](https://www.youtube.com/watch?v=HCR7i5F5L8c)
|
||||
* [Fundamentals in 60 Minutes (video)](http://www.youtube.com/watch?v=i9MHigUZKEM)
|
||||
* [For folks with a jQuery background](http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background)
|
||||
|
||||
### Specific Topics
|
||||
|
||||
#### Application Structure & Style Guides
|
||||
|
||||
* [Angular Styleguide](https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md)
|
||||
* [Architecture, file structure, components, one-way dataflow and best practices](https://github.com/toddmotto/angular-styleguide)
|
||||
* [When to use directives, controllers or services](http://kirkbushell.me/when-to-use-directives-controllers-or-services-in-angular/)
|
||||
* [Service vs Factory](http://blog.thoughtram.io/angular/2015/07/07/service-vs-factory-once-and-for-all.html)
|
||||
|
||||
#### Testing
|
||||
|
||||
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
|
||||
|
||||
#### Mobile
|
||||
|
||||
* [Angular on Mobile Guide](http://www.ng-newsletter.com/posts/angular-on-mobile.html)
|
||||
* [Angular and Cordova](http://devgirl.org/2013/06/10/quick-start-guide-phonegap-and-angularjs/)
|
||||
* [Ionic Framework](http://ionicframework.com/)
|
||||
|
||||
#### Deployment
|
||||
|
||||
##### General
|
||||
|
||||
* **Javascript minification: **[Background](http://thegreenpizza.github.io/2013/05/25/building-minification-safe-angular.js-applications/), [ng-annotate automation tool](https://github.com/olov/ng-annotate)
|
||||
* **Analytics and Logging:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Angulartics (Analytics)](https://github.com/luisfarzati/angulartics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
|
||||
* **SEO:** [By hand](http://www.yearofmoo.com/2012/11/angularjs-and-seo.html), [prerender.io](http://prerender.io/), [Brombone](http://www.brombone.com/), [SEO.js](http://getseojs.com/), [SEO4Ajax](http://www.seo4ajax.com/)
|
||||
|
||||
##### Server-Specific
|
||||
|
||||
* **Django:** [Tutorial](http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework), [Integrating AngularJS with Django](http://django-angular.readthedocs.org/en/latest/integration.html), [Getting Started with Django Rest Framework and AngularJS](http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html)
|
||||
* **FireBase:** [AngularFire](http://angularfire.com/), [Firebase Foundations for AngularJS](http://blog.watchandcode.com/firebase-foundations/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
|
||||
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/developers/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications/), [with Go](https://github.com/GoogleCloudPlatform/appengine-angular-gotodos)
|
||||
* **Hood.ie:** [60 Minutes to Awesome](http://www.roberthorvick.com/2013/06/30/todomvc-angularjs-hood-ie-60-minutes-to-awesome/)
|
||||
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
|
||||
* **Rails: **[Tutorial](http://coderberry.me/blog/2013/04/22/angularjs-on-rails-4-part-1/), [AngularJS with Rails4](https://shellycloud.com/blog/2013/10/how-to-integrate-angularjs-with-rails-4), [angularjs-rails](https://github.com/hiravgandhi/angularjs-rails)
|
||||
* **PHP: **[Building a RESTful web service](http://blog.brunoscopelliti.com/building-a-restful-web-service-with-angularjs-and-php-more-power-with-resource), [End to End with Laravel 4 (video)](http://www.youtube.com/watch?v=hqAyiqUs93c)
|
||||
* **Meteor: **[angular-meteor package](https://github.com/Urigo/angular-meteor)
|
||||
|
||||
### Other Languages
|
||||
* [ES6, Webpack, and JSPM Starter Project](https://github.com/AngularClass/NG6-starter)
|
||||
* [ES6/Typescript Best Practices](https://codepen.io/martinmcwhorter/post/angularjs-1-x-with-typescript-or-es6-best-practices)
|
||||
* [Dart](https://github.com/angular/angular.dart.tutorial/wiki)
|
||||
* [CoffeeScript Tutorial](http://www.coffeescriptlove.com/2013/08/angularjs-and-coffeescript-tutorials.html)
|
||||
|
||||
### More Topics
|
||||
|
||||
* **Security:** [video](https://www.youtube.com/watch?v=18ifoT-Id54)
|
||||
* **Internationalization and Localization:** [Creating multilingual support](http://www.novanet.no/blog/hallstein-brotan/dates/2013/10/creating-multilingual-support-using-angularjs/)
|
||||
* **Authentication/Login: **[Google example](https://developers.google.com/+/photohunt/python), [AngularJS Facebook library](https://github.com/pc035860/angular-easyfb), [Facebook example](http://blog.brunoscopelliti.com/facebook-authentication-in-your-angularjs-web-app), [authentication strategy](http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app), [unix-style authorization](http://frederiknakstad.com/authentication-in-single-page-applications-with-angular-js/)
|
||||
* **Visualization:** [SVG](http://gaslight.co/blog/angular-backed-svgs), [D3.js](http://www.ng-newsletter.com/posts/d3-on-angular.html)
|
||||
* **Realtime Communication: **[Socket.io](http://www.creativebloq.com/javascript/angularjs-collaboration-board-socketio-2132885), [OmniBinder](https://github.com/jeffbcross/omnibinder)
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
* **Getting Started:** [Comparison of the options for starting a new project](http://www.dancancro.com/comparison-of-angularjs-application-starters/)
|
||||
* **Debugging:** [Batarang](https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk?hl=en)
|
||||
* **Editor support:** [Webstorm](http://plugins.jetbrains.com/plugin/6971) (and [video](http://www.youtube.com/watch?v=LJOyrSh1kDU)), [Sublime Text](https://github.com/angular-ui/AngularJS-sublime-package), [Visual Studio](http://madskristensen.net/post/angularjs-intellisense-in-visual-studio-2012)
|
||||
* **Workflow:** [Yeoman.io](https://github.com/yeoman/generator-angular) and [Angular Yeoman Tutorial](http://www.sitepoint.com/kickstart-your-angularjs-development-with-yeoman-grunt-and-bower/)
|
||||
|
||||
## Complementary Libraries
|
||||
|
||||
This is a list of libraries that enhance Angular, add common UI components or integrate with other libraries.
|
||||
You can find a larger list of Angular external libraries at [ngmodules.org](http://ngmodules.org/).
|
||||
|
||||
* **Advanced Routing:** [UI-Router](https://github.com/angular-ui/ui-router)
|
||||
* **Authentication:** [Http Auth Interceptor](https://github.com/witoldsz/angular-http-auth)
|
||||
* **Internationalization:**
|
||||
- [angular-translate](http://angular-translate.github.io)
|
||||
- [angular-gettext](http://angular-gettext.rocketeer.be/)
|
||||
- [angular-localization](http://doshprompt.github.io/angular-localization/)
|
||||
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
|
||||
* **SQL and NoSQL backends:**
|
||||
- [BreezeJS](http://www.breezejs.com/)
|
||||
- [AngularFire](http://angularfire.com/)
|
||||
* **Data Handling**
|
||||
- Local Storage and session: [ngStorage](https://github.com/gsklee/ngStorage)
|
||||
- [angular-cache](https://github.com/jmdobry/angular-cache)
|
||||
- Data Modeling [JS-Data-Angular](https://github.com/js-data/js-data-angular)
|
||||
* **Fileupload:**
|
||||
- [ng-file-upload](https://github.com/danialfarid/ng-file-upload)
|
||||
- [blueimp-fileupload for Angular](https://blueimp.github.io/jQuery-File-Upload/angularjs.html)
|
||||
* **General UI Libraries:**
|
||||
- [Angular Material](https://material.angularjs.org/latest/)
|
||||
- [Angular UI Bootstrap](http://angular-ui.github.io/)
|
||||
- [AngularStrap for Bootstrap 3](http://mgcrea.github.io/angular-strap/)
|
||||
- [KendoUI](http://kendo-labs.github.io/angular-kendo/#/)
|
||||
- [Wijmo](http://wijmo.com/tag/angularjs-2/)
|
||||
* **Specific UI Elements:**
|
||||
- [ngInfiniteScroll](https://sroze.github.io/ngInfiniteScroll/)
|
||||
- [ngTable](https://github.com/esvit/ng-table)
|
||||
- [Angular UI Grid](http://angular-ui.github.io/grid)
|
||||
- [Toaster Notifications](https://github.com/jirikavi/AngularJS-Toaster)
|
||||
- [textAngular Rich Text Editor / contenteditable](http://textangular.com/) (Rich Text Editor /
|
||||
binding to contenteditable)
|
||||
- [Angular UI Map (Google Maps)](https://github.com/angular-ui/ui-map)
|
||||
|
||||
## General Learning Resources
|
||||
|
||||
### Books
|
||||
* [AngularJS: Up and Running](http://www.amazon.com/AngularJS-Running-Enhanced-Productivity-Structured/dp/1491901942) by Brad Green and Shyam Seshadri
|
||||
* [Mastering Web App Development](http://www.amazon.com/Mastering-Web-Application-Development-AngularJS/dp/1782161821) by Pawel Kozlowski and Pete Bacon Darwin
|
||||
* [AngularJS Directives](http://www.amazon.com/AngularJS-Directives-Alex-Vanston/dp/1783280336) by Alex Vanston
|
||||
* [Recipes With AngularJS](http://www.amazon.co.uk/Recipes-Angular-js-Frederik-Dietz-ebook/dp/B00DK95V48) by Frederik Dietz
|
||||
* [Developing an AngularJS Edge](http://www.amazon.com/Developing-AngularJS-Edge-Christopher-Hiller-ebook/dp/B00CJLFF8K) by Christopher Hiller
|
||||
* [ng-book: The Complete Book on AngularJS](http://ng-book.com/) by Ari Lerner
|
||||
* [AngularJS : Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
|
||||
* [AngularJS UI Development](http://www.amazon.com/AngularJS-UI-Development-Amit-Ghart-ebook/dp/B00OXVAK7A) by Amit Gharat and Matthias Nehlsen
|
||||
* [Responsive Web Design with AngularJS](http://www.amazon.com/Responsive-Design-AngularJS-Sandeep-Kumar/dp/178439842X) by Sandeep Kumar Patel
|
||||
* [Professional AngularJS](http://www.amazon.com/Professional-AngularJS-Valeri-Karpov/dp/1118832078/)
|
||||
|
||||
### Videos:
|
||||
* [egghead.io](http://egghead.io/)
|
||||
|
||||
### Courses
|
||||
* **Free online:**
|
||||
[thinkster.io](http://thinkster.io),
|
||||
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1),
|
||||
[CodeSchool](https://www.codeschool.com/courses/shaping-up-with-angular-js)
|
||||
* **Paid online:**
|
||||
[The Angular Course (115 videos that show you how to build a full app)](http://watchandcode.com/courses/angular-course/),
|
||||
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
|
||||
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
|
||||
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html),
|
||||
[WintellectNOW (4 lessons)](http://www.wintellectnow.com/Course/Detail/mastering-angularjs)
|
||||
* **Paid onsite:**
|
||||
[angularbootcamp.com](http://angularbootcamp.com/)
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
|
||||
# Filters
|
||||
|
||||
A filter formats the value of an expression for display to the user. They can be used in view templates,
|
||||
controllers or services and it is easy to define your own filter.
|
||||
Filters format the value of an expression for display to the user. They can be used in view
|
||||
templates, controllers or services. Angular comes with a collection of
|
||||
[built-in filters](api/ng/filter), but it is easy to define your own as well.
|
||||
|
||||
The underlying API is the {@link ng.$filterProvider `filterProvider`}.
|
||||
The underlying API is the {@link ng.$filterProvider}.
|
||||
|
||||
## Using filters in view templates
|
||||
|
||||
@@ -31,6 +32,21 @@ Filters may have arguments. The syntax for this is
|
||||
E.g. the markup `{{ 1234 | number:2 }}` formats the number 1234 with 2 decimal points using the
|
||||
{@link ng.filter:number `number`} filter. The resulting value is `1,234.00`.
|
||||
|
||||
### When filters are executed
|
||||
|
||||
In templates, filters are only executed when their inputs have changed. This is more performant than executing
|
||||
a filter on each {@link ng.$rootScope.Scope#$digest `$digest`} as is the case with {@link guide/expression expressions}.
|
||||
|
||||
There are two exceptions to this rule:
|
||||
|
||||
1. In general, this applies only to filters that take [primitive values](https://developer.mozilla.org/docs/Glossary/Primitive)
|
||||
as inputs. Filters that receive [Objects](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Objects)
|
||||
as input are executed on each `$digest`, as it would be too costly to track if the inputs have changed.
|
||||
|
||||
2. Filters that are marked as `$stateful` are also executed on each $digest.
|
||||
See {@link guide/filter#stateful-filters Stateful filters} for more information. Note that no Angular
|
||||
core filters are $stateful.
|
||||
|
||||
|
||||
## Using filters in controllers, services, and directives
|
||||
|
||||
@@ -93,8 +109,9 @@ as the first argument. Any filter arguments are passed in as additional argument
|
||||
function.
|
||||
|
||||
The filter function should be a [pure function](http://en.wikipedia.org/wiki/Pure_function), which
|
||||
means that it should be stateless and idempotent. Angular relies on these properties and executes
|
||||
the filter only when the inputs to the function change.
|
||||
means that it should be stateless and idempotent, and not rely for example on other Angular services.
|
||||
Angular relies on this contract and will by default execute a filter only when the inputs to the function change.
|
||||
{@link guide/filter#stateful-filters Stateful filters} are possible, but less performant.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
|
||||
@@ -141,7 +158,7 @@ text upper-case.
|
||||
</example>
|
||||
|
||||
|
||||
## Stateful filters
|
||||
### Stateful filters
|
||||
|
||||
It is strongly discouraged to write filters that are stateful, because the execution of those can't
|
||||
be optimized by Angular, which often leads to performance issues. Many stateful filters can be
|
||||
@@ -183,4 +200,4 @@ means that it will be executed one or more times during the each `$digest` cycle
|
||||
|
||||
## Testing custom filters
|
||||
|
||||
See the [phonecat tutorial](http://docs.angularjs.org/tutorial/step_09#test) for an example.
|
||||
See the [phonecat tutorial](http://docs.angularjs.org/tutorial/step_11#testing) for an example.
|
||||
|
||||
@@ -29,6 +29,13 @@ To ensure your Angular application works on IE please consider:
|
||||
|
||||
1. Use `ng-style` tags instead of `style="{{ someCss }}"`. The latter works in Chrome and Firefox
|
||||
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
|
||||
2. For the `type` attribute of buttons, use `ng-attr-type` tags instead of
|
||||
`type="{{ someExpression }}"`. If using the latter, Internet Explorer overwrites the expression
|
||||
2. For the `type` attribute of buttons, use `ng-attr-type` tags instead of
|
||||
`type="{{ someExpression }}"`. If using the latter, Internet Explorer overwrites the expression
|
||||
with `type="submit"` before Angular has a chance to interpolate it.
|
||||
3. For the `value` attribute of progress, use `ng-attr-value` tags instead of
|
||||
`value="{{ someExpression}}"`. If using the latter, Internet Explorer overwrites the expression
|
||||
with `value="0"` before Angular has a chance to interpolate it.
|
||||
4. For the `placeholder` attribute of textarea, use `ng-attr-placeholder` tags instead
|
||||
of `placeholder="{{ someExpression }}"`. If using the latter, Internet Explorer will error
|
||||
on accessing the `nodeValue` on a parentless `TextNode` in Internet Explorer 10 & 11
|
||||
(see [issue 5025](https://github.com/angular/angular.js/issues/5025)).
|
||||
|
||||
@@ -2,22 +2,21 @@
|
||||
@name Developer Guide
|
||||
@description
|
||||
|
||||
# Guide to AngularJS Documentation
|
||||
# Guide to Angular 1 Documentation
|
||||
|
||||
Everything you need to know about AngularJS
|
||||
On this page, you will find a list of official Angular resources on various topics.
|
||||
|
||||
* {@link guide/introduction What is AngularJS?}
|
||||
Just starting out with Angular 1? Try working through our step by step tutorial or try
|
||||
building on our seed project.
|
||||
|
||||
* {@link tutorial/index Official Angular 1 Tutorial}
|
||||
* [Angular Seed](https://github.com/angular/angular-seed)
|
||||
|
||||
Ready to find out more about Angular 1?
|
||||
|
||||
* {@link guide/introduction What is Angular 1?}
|
||||
* {@link guide/concepts Conceptual Overview}
|
||||
|
||||
## Tutorials
|
||||
|
||||
* {@link tutorial/index Official AngularJS Tutorial}
|
||||
* [10 Reasons Why You Should Use AngularJS](http://www.sitepoint.com/10-reasons-use-angularjs/)
|
||||
* [10 Reasons Why Developers Should Learn AngularJS](http://wintellect.com/blogs/jlikness/10-reasons-web-developers-should-learn-angularjs)
|
||||
* [Design Principles of AngularJS (video)](https://www.youtube.com/watch?v=HCR7i5F5L8c)
|
||||
* [Fundamentals in 60 Minutes (video)](http://www.youtube.com/watch?v=i9MHigUZKEM)
|
||||
* [For folks with a jQuery background](http://stackoverflow.com/questions/14994391/how-do-i-think-in-angularjs-if-i-have-a-jquery-background)
|
||||
|
||||
## Core Concepts
|
||||
|
||||
### Templates
|
||||
@@ -26,134 +25,68 @@ In Angular applications, you move the job of filling page templates with data fr
|
||||
|
||||
* {@link guide/databinding Data binding}
|
||||
* {@link guide/expression Expressions}
|
||||
* {@link guide/interpolation Interpolation}
|
||||
* {@link guide/directive Directives}
|
||||
* {@link ngRoute.$route Views and routes (see the example)}
|
||||
* {@link guide/filter Filters}
|
||||
* {@link guide/forms Forms} and [Concepts of AngularJS Forms](http://mrbool.com/the-concepts-of-angularjs-forms/29117)
|
||||
* {@link guide/compiler HTML compiler}
|
||||
* {@link guide/forms Forms}
|
||||
|
||||
### Application Structure
|
||||
|
||||
* **Blog post: **[When to use directives, controllers or services](http://kirkbushell.me/when-to-use-directives-controllers-or-services-in-angular/)
|
||||
* **App wiring:** {@link guide/di Dependency injection}
|
||||
* **Exposing model to templates:** {@link guide/scope Scopes}
|
||||
* **Bootstrap:** {@link guide/bootstrap Bootstrapping an app}
|
||||
* **Communicating with servers:** {@link ng.$http $http}, {@link ngResource.$resource $resource}
|
||||
|
||||
### Other AngularJS Features
|
||||
### Other Features
|
||||
|
||||
* **Animation:** {@link guide/animations Core concepts}, {@link ngAnimate ngAnimate API}, and [Animation in AngularJS 1.2](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html)
|
||||
* **Animation:** {@link guide/animations Core concepts}, {@link ngAnimate ngAnimate API}
|
||||
* **Security:** {@link guide/security Security Docs}, {@link ng.$sce Strict Contextual Escaping}, {@link ng.directive:ngCsp Content Security Policy}, {@link ngSanitize.$sanitize $sanitize}, [video](https://www.youtube.com/watch?v=18ifoT-Id54)
|
||||
* **Internationalization and Localization:** {@link guide/i18n Angular Guide to i18n and l10n}, {@link ng.filter:date date filter}, {@link ng.filter:currency currency filter}, [Creating multilingual support](http://www.novanet.no/blog/hallstein-brotan/dates/2013/10/creating-multilingual-support-using-angularjs/)
|
||||
* **Mobile:** {@link ngTouch Touch events}
|
||||
* **Touch events:** {@link ngTouch Touch events}
|
||||
* **Accessibility:** {@link guide/accessibility ngAria}
|
||||
|
||||
### Testing
|
||||
|
||||
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/unit-testing Unit testing}, {@link guide/services#unit-testing Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
|
||||
* **Scenario testing:** [Protractor](https://github.com/angular/protractor)
|
||||
* **Unit testing:** [Karma](http://karma-runner.github.io), {@link guide/unit-testing Unit testing}, {@link guide/services#unit-testing Testing services},
|
||||
* **End-to-End Testing:** [Protractor](https://github.com/angular/protractor), {@link guide/e2e-testing e2e testing guide}
|
||||
|
||||
## Specific Topics
|
||||
|
||||
* **Login: **[Google example](https://developers.google.com/+/photohunt/python), [AngularJS Facebook library](https://github.com/pc035860/angular-easyfb), [Facebook example](http://blog.brunoscopelliti.com/facebook-authentication-in-your-angularjs-web-app), [authentication strategy](http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app), [unix-style authorization](http://frederiknakstad.com/authentication-in-single-page-applications-with-angular-js/)
|
||||
* **Mobile:** [Angular on Mobile Guide](http://www.ng-newsletter.com/posts/angular-on-mobile.html), [PhoneGap](http://devgirl.org/2013/06/10/quick-start-guide-phonegap-and-angularjs/)
|
||||
* **Other Languages:** [CoffeeScript](http://www.coffeescriptlove.com/2013/08/angularjs-and-coffeescript-tutorials.html), [Dart](https://github.com/angular/angular.dart.tutorial/wiki)
|
||||
* **Realtime: **[Socket.io](http://www.creativebloq.com/javascript/angularjs-collaboration-board-socketio-2132885), [OmniBinder](https://github.com/jeffbcross/omnibinder)
|
||||
* **Visualization:** [SVG](http://gaslight.co/blog/angular-backed-svgs), [D3.js](http://www.ng-newsletter.com/posts/d3-on-angular.html)
|
||||
* **Local Storage and session:** [ngStorage](https://github.com/gsklee/ngStorage)
|
||||
## Community Resources
|
||||
|
||||
## Tools
|
||||
We have set up a guide to many resources provided by the community, where you can find lots
|
||||
of additional information and material on these topics, a list of complimentary libraries, and much more.
|
||||
|
||||
* **Getting Started:** [Comparison of the options for starting a new project](http://www.dancancro.com/comparison-of-angularjs-application-starters/)
|
||||
* **Debugging:** [Batarang](https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehidhipcmcojjgiloacoafjmpfk?hl=en)
|
||||
* **Testing:** [Karma](http://karma-runner.github.io), [Protractor](https://github.com/angular/protractor)
|
||||
* **Editor support:** [Webstorm](http://plugins.jetbrains.com/plugin/6971) (and [video](http://www.youtube.com/watch?v=LJOyrSh1kDU)), [Sublime Text](https://github.com/angular-ui/AngularJS-sublime-package), [Visual Studio](http://madskristensen.net/post/angularjs-intellisense-in-visual-studio-2012)
|
||||
* **Workflow:** [Yeoman.io](https://github.com/yeoman/generator-angular) and [Angular Yeoman Tutorial](http://www.sitepoint.com/kickstart-your-angularjs-development-with-yeoman-grunt-and-bower/)
|
||||
* {@link guide/external-resources External Angular 1 resources}
|
||||
|
||||
## Complementary Libraries
|
||||
|
||||
This is a short list of libraries with specific support and documentation for working with Angular. You can find a full list of all known Angular external libraries at [ngmodules.org](http://ngmodules.org/).
|
||||
|
||||
* **Internationalization:** [angular-translate](http://angular-translate.github.io), [angular-gettext](http://angular-gettext.rocketeer.be/), [angular-localization](http://doshprompt.github.io/angular-localization/)
|
||||
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
|
||||
* **SQL and NoSQL backends:** [BreezeJS](http://www.breezejs.com/), [AngularFire](http://angularfire.com/)
|
||||
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/), [ngTagsInput](https://github.com/mbenford/ngTagsInput)
|
||||
* **Advanced Routing:** [UI-Router](https://github.com/angular-ui/ui-router)
|
||||
* **Maps:** [UI-Map (Google Maps)](https://github.com/angular-ui/ui-map)
|
||||
|
||||
## Deployment
|
||||
|
||||
### General
|
||||
|
||||
* **Docs Page:** {@link guide/production Running an AngularJS App in Production}
|
||||
* **Javascript minification: **[Background](http://thegreenpizza.github.io/2013/05/25/building-minification-safe-angular.js-applications/), [ng-annotate automation tool](https://github.com/olov/ng-annotate)
|
||||
* **Analytics and Logging:** [Angularyitcs (Google Analytics)](http://ngmodules.org/modules/angularytics), [Angulartics (Analytics)](https://github.com/luisfarzati/angulartics), [Logging Client-Side Errors](http://www.bennadel.com/blog/2542-Logging-Client-Side-Errors-With-AngularJS-And-Stacktrace-js.htm)
|
||||
* **SEO:** [By hand](http://www.yearofmoo.com/2012/11/angularjs-and-seo.html), [prerender.io](http://prerender.io/), [Brombone](http://www.brombone.com/), [SEO.js](http://getseojs.com/), [SEO4Ajax](http://www.seo4ajax.com/)
|
||||
|
||||
### Server-Specific
|
||||
|
||||
* **Django:** [Tutorial](http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework), [Integrating AngularJS with Django](http://django-angular.readthedocs.org/en/latest/integration.html), [Getting Started with Django Rest Framework and AngularJS](http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html)
|
||||
* **FireBase:** [AngularFire](http://angularfire.com/), [Firebase Foundations for AngularJS](http://blog.watchandcode.com/firebase-foundations/), [Realtime Apps with AngularJS and FireBase (video)](http://www.youtube.com/watch?v=C7ZI7z7qnHU)
|
||||
* **Google Cloud Platform: **[with Cloud Endpoints](https://cloud.google.com/developers/articles/angularjs-cloud-endpoints-recipe-for-building-modern-web-applications/), [with Go](https://github.com/GoogleCloudPlatform/appengine-angular-gotodos)
|
||||
* **Hood.ie:** [60 Minutes to Awesome](http://www.roberthorvick.com/2013/06/30/todomvc-angularjs-hood-ie-60-minutes-to-awesome/)
|
||||
* **MEAN Stack: **[Blog post](http://blog.mongodb.org/post/49262866911/the-mean-stack-mongodb-expressjs-angularjs-and), [Setup](http://thecodebarbarian.wordpress.com/2013/07/22/introduction-to-the-mean-stack-part-one-setting-up-your-tools/), [GDL Video](https://developers.google.com/live/shows/913996610)
|
||||
* **Rails: **[Tutorial](http://coderberry.me/blog/2013/04/22/angularjs-on-rails-4-part-1/), [AngularJS with Rails4](https://shellycloud.com/blog/2013/10/how-to-integrate-angularjs-with-rails-4), [angularjs-rails](https://github.com/hiravgandhi/angularjs-rails)
|
||||
* **PHP: **[Building a RESTful web service](http://blog.brunoscopelliti.com/building-a-restful-web-service-with-angularjs-and-php-more-power-with-resource), [End to End with Laravel 4 (video)](http://www.youtube.com/watch?v=hqAyiqUs93c)
|
||||
* **Meteor: **[angular-meteor package](https://github.com/Urigo/angular-meteor)
|
||||
|
||||
## Learning Resources
|
||||
|
||||
### Books
|
||||
* [AngularJS: Up and Running](http://www.amazon.com/AngularJS-Running-Enhanced-Productivity-Structured/dp/1491901942) by Brad Green and Shyam Seshadri
|
||||
* [Mastering Web App Development](http://www.amazon.com/Mastering-Web-Application-Development-AngularJS/dp/1782161821) by Pawel Kozlowski and Pete Bacon Darwin
|
||||
* [AngularJS Directives](http://www.amazon.com/AngularJS-Directives-Alex-Vanston/dp/1783280336) by Alex Vanston
|
||||
* [Recipes With AngularJS](http://www.amazon.co.uk/Recipes-Angular-js-Frederik-Dietz-ebook/dp/B00DK95V48) by Frederik Dietz
|
||||
* [Developing an AngularJS Edge](http://www.amazon.com/Developing-AngularJS-Edge-Christopher-Hiller-ebook/dp/B00CJLFF8K) by Christopher Hiller
|
||||
* [ng-book: The Complete Book on AngularJS](http://ng-book.com/) by Ari Lerner
|
||||
* [AngularJS : Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
|
||||
* [AngularJS UI Development](http://www.amazon.com/AngularJS-UI-Development-Amit-Ghart-ebook/dp/B00OXVAK7A) by Amit Gharat and Matthias Nehlsen
|
||||
* [Responsive Web Design with AngularJS](http://www.amazon.com/Responsive-Design-AngularJS-Sandeep-Kumar/dp/178439842X) by Sandeep Kumar Patel
|
||||
* [Professional AngularJS](http://www.amazon.com/Professional-AngularJS-Valeri-Karpov/dp/1118832078/)
|
||||
|
||||
### Videos:
|
||||
* [egghead.io](http://egghead.io/)
|
||||
* [Angular on YouTube](http://youtube.com/angularjs)
|
||||
* [Firebase Foundations for AngularJS](http://blog.watchandcode.com/firebase-foundations/)
|
||||
|
||||
### Courses
|
||||
* **Free online:**
|
||||
[thinkster.io](http://thinkster.io),
|
||||
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1),
|
||||
[CodeSchool](https://www.codeschool.com/courses/shaping-up-with-angular-js)
|
||||
* **Paid online:**
|
||||
[The Angular Course (115 videos that show you how to build a full app)](http://watchandcode.com/courses/angular-course/),
|
||||
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
|
||||
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
|
||||
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html),
|
||||
[WintellectNOW (4 lessons)](http://www.wintellectnow.com/Course/Detail/mastering-angularjs)
|
||||
* **Paid onsite:**
|
||||
[angularbootcamp.com](http://angularbootcamp.com/)
|
||||
|
||||
## Getting Help
|
||||
|
||||
The recipe for getting help on your unique issue is to create an example that could work (even if it doesn't) in a shareable example on [Plunker](http://plnkr.co/), [JSFiddle](http://jsfiddle.net/), or similar site and then post to one of the following:
|
||||
|
||||
* [Stackoverflow.com](http://stackoverflow.com/search?q=angularjs)
|
||||
* [AngularJS mailing list](https://groups.google.com/forum/#!forum/angular)
|
||||
* [AngularJS IRC channel](http://webchat.freenode.net/?channels=angularjs&uio=d4)
|
||||
* [Angular 1 mailing list](https://groups.google.com/forum/#!forum/angular)
|
||||
* [Angular 1 IRC channel](http://webchat.freenode.net/?channels=angularjs&uio=d4)
|
||||
|
||||
## Social Channels
|
||||
## Official Communications
|
||||
|
||||
* **Daily updates:** [Google+](https://plus.google.com/u/0/+AngularJS) or [Twitter](https://twitter.com/angularjs)
|
||||
* **Weekly newsletter:** [ng-newsletter](http://www.ng-newsletter.com/)
|
||||
* **Meetups: **[meetup.com](http://www.meetup.com/find/?keywords=angularJS&radius=Infinity&userFreeform=San+Francisco%2C+CA&mcId=z94108&mcName=San+Francisco%2C+CA&sort=member_count&eventFilter=mysugg)
|
||||
* **Official news and releases: **[AngularJS Blog](http://blog.angularjs.org/)
|
||||
Official announcements, news and releases are posted to our blog, G+ and Twitter:
|
||||
|
||||
## Contributing to AngularJS
|
||||
* [Angular Blog](http://blog.angularjs.org/)
|
||||
* [Google+](https://plus.google.com/u/0/+AngularJS)
|
||||
* [Twitter](https://twitter.com/angularjs)
|
||||
* [Angular on YouTube](http://youtube.com/angularjs)
|
||||
|
||||
Though we have a core group of core contributors at Google, Angular is an open source project with hundreds of contributors. We'd love you to be one of them. When you're ready, please read the {@link misc/contribute Guide for contributing to AngularJS}.
|
||||
## Contributing to Angular 1
|
||||
|
||||
## Final Bits
|
||||
Though we have a core group of core contributors at Google, Angular is an open source project with hundreds of contributors.
|
||||
We'd love you to be one of them. When you're ready, please read the {@link misc/contribute Guide for contributing to Angular}.
|
||||
|
||||
Didn't find what you're looking for here? Check out [AngularJS-Learning](https://github.com/jmcunningham/AngularJS-Learning) for an even more comprehensive list of links to videos, tutorials, and blog posts.
|
||||
## Something Missing?
|
||||
|
||||
If you have awesome AngularJS resources that belong on this page, please tell us about them on [Google+](https://plus.google.com/u/0/+AngularJS) or [Twitter](https://twitter.com/angularjs).
|
||||
Didn't find what you're looking for here? Check out the {@link guide/external-resources External Angular 1 resources guide}.
|
||||
|
||||
If you have awesome Angular 1 resources that belong on that page, please tell us about them on
|
||||
[Google+](https://plus.google.com/u/0/+AngularJS) or [Twitter](https://twitter.com/angularjs).
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ would be lost, because the browser ignores the attribute value.
|
||||
In the following example, the interpolation information would be ignored and the browser would simply
|
||||
interpret the attribute as present, meaning that the button would always be disabled.
|
||||
|
||||
```html
|
||||
```html
|
||||
Disabled: <input type="checkbox" ng-model="isDisabled" />
|
||||
<button disabled="{{isDisabled}}">Disabled</button>
|
||||
```
|
||||
@@ -49,7 +49,7 @@ For this reason, Angular provides special `ng`-prefixed directives for the follo
|
||||
These directives take an expression inside the attribute, and set the corresponding boolean attribute
|
||||
to true when the expression evaluates to truthy.
|
||||
|
||||
```html
|
||||
```html
|
||||
Disabled: <input type="checkbox" ng-model="isDisabled" />
|
||||
<button ng-disabled="isDisabled">Disabled</button>
|
||||
```
|
||||
@@ -104,7 +104,20 @@ can be used with `ngAttr` instead. The following is a list of known problematic
|
||||
- **size** in `<select>` elements (see [issue 1619](https://github.com/angular/angular.js/issues/1619))
|
||||
- **placeholder** in `<textarea>` in Internet Explorer 10/11 (see [issue 5025](https://github.com/angular/angular.js/issues/5025))
|
||||
- **type** in `<button>` in Internet Explorer 11 (see [issue 14117](https://github.com/angular/angular.js/issues/5025))
|
||||
- **value** in `<progress>` in Internet Explorer = 11 (see [issue 7218](https://github.com/angular/angular.js/issues/7218))
|
||||
|
||||
## Known Issues
|
||||
|
||||
### Dynamically changing an interpolated value
|
||||
|
||||
You should avoid dynamically changing the content of an interpolated string (e.g. attribute value
|
||||
or text node). Your changes are likely to be overwriten, when the original string gets evaluated.
|
||||
This restriction applies to both directly changing the content via JavaScript or indirectly using a
|
||||
directive.
|
||||
|
||||
For example, you should not use interpolation in the value of the `style` attribute (e.g.
|
||||
`style="color: {{ 'orange' }}; font-weight: {{ 'bold' }};"`) **and** at the same time use a
|
||||
directive that changes the content of that attributte, such as `ngStyle`.
|
||||
|
||||
### Embedding interpolation markup inside expressions
|
||||
|
||||
|
||||
@@ -514,7 +514,7 @@ angular.module('myApp').directive('form', function() {
|
||||
});
|
||||
```
|
||||
|
||||
### Templating (`ngRepeat`, `$compile`)
|
||||
### Templating (`ngRepeat`, `$compile`, `ngInclude`)
|
||||
|
||||
#### ngRepeat
|
||||
|
||||
@@ -545,6 +545,44 @@ Due to [62d514b](https://github.com/angular/angular.js/commit/62d514b06937cc7dd8
|
||||
returning an object from a controller constructor function will now override the scope. Views that use the
|
||||
controllerAs method will no longer get the this reference, but the returned object.
|
||||
|
||||
#### ngInclude
|
||||
Due to [3c6e8ce044446735eb2e70d0061db8c6db050289](https://github.com/angular/angular.js/commit/3c6e8ce044446735eb2e70d0061db8c6db050289), the `src` attribute of ngInclude no longer accepts an
|
||||
expression that returns the result of `$sce.trustAsResourceUrl`. This will now cause an infinite digest:
|
||||
|
||||
Before:
|
||||
```html
|
||||
<div ng-include="findTemplate('https://example.com/templates/myTemplate.html')"></div>
|
||||
```
|
||||
|
||||
```js
|
||||
$scope.findTemplate = function(templateName) {
|
||||
return $sce.trustAsResourceUrl(templateName);
|
||||
};
|
||||
```
|
||||
|
||||
To migrate, either cache the result of `trustAsResourceUrl()`, or put the template url in the resource
|
||||
whitelist in the `config()` function:
|
||||
|
||||
After:
|
||||
|
||||
```js
|
||||
var templateCache = {};
|
||||
$scope.findTemplate = function(templateName) {
|
||||
if (!templateCache[templateName]) {
|
||||
templateCache[templateName] = $sce.trustAsResourceUrl(templateName);
|
||||
}
|
||||
|
||||
return templateCache[templateName];
|
||||
};
|
||||
|
||||
// Alternatively, use `$sceDelegateProvider.resourceUrlWhitelist()`, which means you don't
|
||||
// have to use `$sce.trustAsResourceUrl()` at all:
|
||||
|
||||
angular.module('myApp', []).config(function($sceDelegateProvider) {
|
||||
$sceDelegateProvider.resourceUrlWhitelist(['self', 'https://example.com/templates/**'])
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
### Cookies (`ngCookies`)
|
||||
|
||||
@@ -583,8 +621,6 @@ has been moved to `$cookies`, to which `$cookieStore` now simply
|
||||
delegates calls.
|
||||
|
||||
|
||||
|
||||
|
||||
### Server Requests (`$http`)
|
||||
|
||||
Due to [5da1256](https://github.com/angular/angular.js/commit/5da1256fc2812d5b28fb0af0de81256054856369),
|
||||
@@ -616,8 +652,6 @@ $http.get(url, {
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
### Filters (`filter`, `limitTo`)
|
||||
|
||||
#### `filter` filter
|
||||
@@ -636,7 +670,6 @@ Now, instead of returning empty object/array, it returns unchanged input.
|
||||
|
||||
|
||||
|
||||
|
||||
## Migrating from 1.2 to 1.3
|
||||
|
||||
### Controllers
|
||||
|
||||
@@ -75,8 +75,8 @@ that you break your application to multiple modules like this:
|
||||
* And an application level module which depends on the above modules and contains any
|
||||
initialization code.
|
||||
|
||||
You can find a community
|
||||
[style guide](https://github.com/johnpapa/angular-styleguide) to help yourself when application grows.
|
||||
You can find a community [style guide](https://github.com/johnpapa/angular-styleguide) to help
|
||||
yourself when application grows.
|
||||
|
||||
The above is a suggestion. Tailor it to your needs.
|
||||
|
||||
|
||||
@@ -87,6 +87,10 @@ Protection from JSON Hijacking is provided if the server prefixes all JSON reque
|
||||
Angular will automatically strip the prefix before processing it as JSON.
|
||||
For more information please visit {@link $http#json-vulnerability-protection JSON Hijacking Protection}.
|
||||
|
||||
Bear in mind that calling `$http.jsonp`, like in [our Yahoo! finance example](https://docs.angularjs.org/guide/concepts#accessing-the-backend),
|
||||
gives the remote server (and, if the request is not secured, any Man-in-the-Middle attackers)
|
||||
instant remote code execution in your application: the result of these requests is handed off
|
||||
to the browser as regular `<script>` tag.
|
||||
|
||||
## Strict Contextual Escaping
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
@sortOrder 410
|
||||
@description
|
||||
|
||||
# Unit Testing
|
||||
|
||||
JavaScript is a dynamically typed language which comes with great power of expression, but it also
|
||||
comes with almost no help from the compiler. For this reason we feel very strongly that any code
|
||||
written in JavaScript needs to come with a strong set of tests. We have built many features into
|
||||
|
||||
@@ -53,7 +53,7 @@ minified AngularJS files:
|
||||
|
||||
```shell
|
||||
# Clone your Github repository:
|
||||
git clone "git@github.com:<github username>/angular.js.git"
|
||||
git clone https://github.com/<github username>/angular.js.git
|
||||
|
||||
# Go to the AngularJS directory:
|
||||
cd angular.js
|
||||
|
||||
@@ -2,85 +2,122 @@
|
||||
@name Downloading
|
||||
@description
|
||||
|
||||
# Including angular scripts from the Google CDN
|
||||
The quickest way to get started is to point your html `<script>` tag to a Google CDN URL.
|
||||
# Including Angular scripts from the Google CDN
|
||||
The quickest way to get started is to point your html `<script>` tag to a
|
||||
[Google CDN](https://developers.google.com/speed/libraries/#angularjs) URL.
|
||||
This way, you don't have to download anything or maintain a local copy.
|
||||
|
||||
There are two types of angular script URLs you can point to, one for development and one for
|
||||
There are two types of Angular script URLs you can point to, one for development and one for
|
||||
production:
|
||||
|
||||
* __angular.js__ — This is the human-readable, non-minified version, suitable for web
|
||||
development.
|
||||
* __angular.js__ — This is the human-readable, non-minified version, suitable for web development.
|
||||
* __angular.min.js__ — This is the minified version, which we strongly suggest you use in
|
||||
production.
|
||||
production.
|
||||
|
||||
To point your code to an angular script on the Google CDN server, use the following template. This
|
||||
example points to the minified version 1.4.5:
|
||||
To point your code to an angular script on the Google CDN server, use the following template. This
|
||||
example points to the minified version 1.5.6:
|
||||
|
||||
```
|
||||
```html
|
||||
<!doctype html>
|
||||
<html ng-app>
|
||||
<head>
|
||||
<title>My Angular App</title>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Note that only versions 1.0.1 and above are available on the CDN, if you need an earlier version
|
||||
you can use the <http://code.angularjs.org/> URL which was the previous recommended location for
|
||||
hosted code source. If you're still using the angular server you should switch to the CDN version
|
||||
for even faster loading times.
|
||||
<div class="alert alert-info">
|
||||
Note that only versions 1.0.1 and above are available on the CDN. If you need an earlier version
|
||||
(which you shouldn't) you can use the https://code.angularjs.org/ URL, which was the previous
|
||||
recommended location for hosted code source. If you're still using the Angular server you should
|
||||
switch to the CDN version for even faster loading times.
|
||||
</div>
|
||||
|
||||
<br />
|
||||
# Downloading and hosting angular files locally
|
||||
This option is for those who want to work with angular offline, or those who want to host the
|
||||
angular files on their own servers.
|
||||
This option is for those who want to work with Angular offline, or those who want to host the
|
||||
Angular files on their own servers.
|
||||
|
||||
If you navigate to <http://code.angularjs.org/>, you'll see a directory listing with all of the
|
||||
angular versions since we started releasing versioned build artifacts (quite late in the project
|
||||
lifetime). Each directory contains all artifacts that we released for a particular version.
|
||||
Download the version you want and have fun.
|
||||
If you navigate to https://code.angularjs.org/, you'll see a directory listing with all of the
|
||||
Angular versions since we started releasing versioned build artifacts. Each directory contains all
|
||||
artifacts that we released for a particular version. Download the version you want and have fun.
|
||||
|
||||
Each directory under <http://code.angularjs.org/> includes the following set of files:
|
||||
<div class="alert alert-warning">
|
||||
You can ignore directories starting with `2.` (e.g. `2.0.0-beta.17`) — they are not related to
|
||||
AngularJS. They contain build artifacts from [Angular 2](https://angular.io) versions.
|
||||
</div>
|
||||
|
||||
* __`angular.js`__ — This file is non-obfuscated, non-minified, and human-readable by
|
||||
opening it in any editor or browser. In order to get better error messages during development, you
|
||||
should always use this non-minified angular script.
|
||||
<br />
|
||||
Each directory under https://code.angularjs.org/ includes a set of files that comprise the
|
||||
corresponding version. All JavaScript files (except for `angular-mocks` which is only used during
|
||||
development) come in two flavors — one suitable for development, the other for production:
|
||||
|
||||
* __`angular.min.js`__ — This is a minified and obfuscated version of
|
||||
`angular.js` created with the Closure compiler. Use this version for production in order
|
||||
to minimize the size of the application that is downloaded by your user's browser.
|
||||
* __`<filename>.js`__ — These files are non-obfuscated, non-minified, and human-readable by opening
|
||||
them in any editor or browser. In order to get better error messages during development, you
|
||||
should always use these non-minified scripts.
|
||||
|
||||
* __`angular.zip`__ — This is a zip archive that contains all of the files released
|
||||
for this angular version. Use this file to get everything in a single download.
|
||||
* __`<filename>.min.js`__ — These are minified and obfuscated versions, created with the
|
||||
[Closure compiler](https://developers.google.com/closure/compiler/). Use these versions for
|
||||
production in order to minimize the size of the application that is downloaded by your user's
|
||||
browser.
|
||||
|
||||
* __`angular-mocks.js`__ — This file contains an implementation of mocks that makes
|
||||
testing angular apps even easier. Your unit/integration test harness should load this file after
|
||||
`angular.js` is loaded.
|
||||
* __`<filename>.min.js.map`__ — These are
|
||||
[sourcemap files](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/). You can
|
||||
serve them alongside the `.min.js` files in order to be able to debug the minified code (e.g. on a
|
||||
production deployment) more easily, but without impacting performance.
|
||||
|
||||
* __`angular-scenario.js`__ — This file is a very nifty JavaScript file that allows you
|
||||
to write and execute end-to-end tests for angular applications.
|
||||
<br />
|
||||
The set of files included in each version directory are:
|
||||
|
||||
* __`angular-loader.min.js`__ — Module loader for Angular modules. If you are loading multiple script files containing
|
||||
Angular modules, you can load them asynchronously and in any order as long as you load this file first. Often the
|
||||
contents of this file are copy&pasted into the `index.html` to avoid even the initial request to `angular-loader.min.js`.
|
||||
See [angular-seed](https://github.com/angular/angular-seed/blob/master/app/index-async.html) for an example of usage.
|
||||
* __`angular.zip`__ — This is a zip archive that contains all of the files released for this Angular
|
||||
version. Use this file to get everything in a single download.
|
||||
|
||||
* __Additional Angular modules:__ optional modules with additional functionality. These files should be loaded
|
||||
after the core `angular.js` file:
|
||||
* __`angular-animate.js`__ - Enable animation support
|
||||
* __`angular-cookies.js`__ - A convenient wrapper for reading and writing browser cookies
|
||||
* __`angular-resource.js`__ - Interaction support with RESTful services via the $resource service
|
||||
* __`angular-route.js`__ - Routing and deeplinking services and directives for angular apps
|
||||
* __`angular-sanitize.js`__ - Functionality to sanitize HTML
|
||||
* __`angular-touch.js`__ - Touch events and other helpers for touch-enabled devices
|
||||
* __`angular.js`__ — The core Angular framework. This is all you need to get your Angular app
|
||||
running.
|
||||
|
||||
* __`angular-csp.css`__ — You only need this file if you are using
|
||||
[CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP). See
|
||||
{@link directive:ngCsp here} for more info.
|
||||
|
||||
* __`angular-mocks.js`__ — This file contains an implementation of mocks that makes testing angular
|
||||
apps even easier. Your unit/integration test harness should load this file after `angular.js` is
|
||||
loaded.
|
||||
|
||||
* __`angular-loader.js`__ — Module loader for Angular modules. If you are loading multiple
|
||||
script files containing Angular modules, you can load them asynchronously and in any order as long
|
||||
as you load this file first. Often the contents of this file are copy&pasted into the `index.html`
|
||||
to avoid even the initial request to `angular-loader[.min].js`.
|
||||
See [angular-seed](https://github.com/angular/angular-seed/blob/master/app/index-async.html) for
|
||||
an example of usage.
|
||||
|
||||
* __Additional Angular modules:__ Optional modules with additional functionality. These files
|
||||
should be loaded after the core `angular[.min].js` file:
|
||||
* __`angular-animate.js`__ — Enable animation support. ({@link module:ngAnimate API docs})
|
||||
* __`angular-aria.js`__ — Make your apps [accessible](http://www.w3.org/TR/wai-aria/) to users of
|
||||
assistive technologies. ({@link module:ngAria API docs})
|
||||
* __`angular-cookies.js`__ — A convenient wrapper for reading and writing browser cookies.
|
||||
({@link module:ngCookies API docs})
|
||||
* __`angular-message-format.js`__ — Enhanced support for pluralization and gender specific
|
||||
messages in interpolated text. ({@link module:ngMessageFormat API docs})
|
||||
* __`angular-messages.js`__ — Enhanced support for displaying validation messages.
|
||||
({@link module:ngMessages API docs})
|
||||
* __`angular-parse-ext.js`__ — Allow Unicode characters in identifiers inside Angular expressions.
|
||||
({@link module:ngParseExt API docs})
|
||||
* __`angular-resource.js`__ — Easy interaction with RESTful services.
|
||||
({@link module:ngResource API docs})
|
||||
* __`angular-route.js`__ — Routing and deep-linking services and directives for Angular apps.
|
||||
({@link module:ngRoute API docs})
|
||||
* __`angular-sanitize.js`__ — Functionality to sanitize HTML. ({@link module:ngSanitize API docs})
|
||||
* __`angular-touch.js`__ — Touch events and other helpers for touch-enabled devices.
|
||||
({@link module:ngTouch API docs})
|
||||
|
||||
|
||||
* __`docs`__ — this directory contains all the files that compose the
|
||||
<http://docs.angularjs.org/> documentation app. These files are handy to see the older version of
|
||||
our docs, or even more importantly, view the docs offline.
|
||||
* __`docs/`__ — This directory contains all the files that compose the https://docs.angularjs.org/
|
||||
documentation app. These files are handy to see the older versions of our docs, or even more
|
||||
importantly, view the docs offline.
|
||||
|
||||
* __`i18n`__ - this directory contains locale specific `ngLocale` angular modules to override the defaults
|
||||
defined in the `ng` module.
|
||||
* __`i18n/`__ - This directory contains [locale specific](https://docs.angularjs.org/guide/i18n)
|
||||
`ngLocale` Angular modules to override the defaults defined in the main `ng` module.
|
||||
|
||||
@@ -199,16 +199,16 @@ Voilà! The resulting output should look the same, but let's see what we have ga
|
||||
# Testing
|
||||
|
||||
Although we have combined our controller with a template into a component, we still can (and should)
|
||||
unit test the controller separately, since this is where are application logic and data reside.
|
||||
unit test the controller separately, since this is where our application logic and data reside.
|
||||
|
||||
In order to retrieve and instantiate a component's controller, Angular provides the
|
||||
{@link ngMock.$componentController $componentController} service.
|
||||
|
||||
<div class="alert alert-info">
|
||||
The `$controller` service that we used in the previous step, can only instantiate controllers that
|
||||
where registered by name, using the `.controller()` method. We could have registered our component
|
||||
controller this way too, if we wanted to. Instead, we chose to define it inline — inside the
|
||||
CDO — to keep things localized, but either way works equally well.
|
||||
The `$controller` service that we used in the previous step can only instantiate controllers that
|
||||
were registered by name, using the `.controller()` method. We could have registered our component
|
||||
controller this way, too, if we wanted to. Instead, we chose to define it inline — inside
|
||||
the CDO — to keep things localized, but either way works equally well.
|
||||
</div>
|
||||
|
||||
<br />
|
||||
@@ -268,7 +268,7 @@ Same as before, execute `npm test` to run the tests and then watch the files for
|
||||
|
||||
# Summary
|
||||
|
||||
You have learned how to organize your application and presentation logic into isolated reusable
|
||||
You have learned how to organize your application and presentation logic into isolated, reusable
|
||||
components. Let's go to {@link step_04 step 4} to learn how to organize our code in directories and
|
||||
files, so it remains easy to locate as our application grows.
|
||||
|
||||
|
||||
@@ -119,8 +119,8 @@ Yeah, you guessed it: There is a better way!
|
||||
|
||||
Each feature/section, will declare its own module and have all related entities registered there.
|
||||
The main module (`phonecatApp`) will declare a dependency on each feature/section module. Now,
|
||||
all it takes to reuse the same code on new project is copying the feature directory over and adding
|
||||
the feature module as a dependency in the new project's main module.
|
||||
all it takes to reuse the same code on a new project is copying the feature directory over and
|
||||
adding the feature module as a dependency in the new project's main module.
|
||||
|
||||
Here is what our `phoneList` feature will look like after this change:
|
||||
|
||||
@@ -171,7 +171,7 @@ will make all entities registered on `phoneList` available on `phonecatApp` as w
|
||||
|
||||
<div class="alert alert-info">
|
||||
<p>
|
||||
Don't forget to also update your `index.html` adding a `<script>` tag for each JacaScript file
|
||||
Don't forget to also update your `index.html` adding a `<script>` tag for each JavaScript file
|
||||
we have created. This might seem tedious, but is totally worth it.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
@@ -87,7 +87,7 @@ feature, we can implement [deep linking][deep-linking], which lets us utilize th
|
||||
|
||||
<div class="alert alert-success">
|
||||
<p>
|
||||
`ngRoute` allows as to associate a controller and a template with a specific URL (or URL
|
||||
`ngRoute` lets us associate a controller and a template with a specific URL (or URL
|
||||
pattern). This is pretty close to what we did with `ngController` and `index.html` back in
|
||||
{@link step_02 step 2}.
|
||||
</p>
|
||||
|
||||
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
@@ -27,17 +27,17 @@
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1458"
|
||||
inkscape:window-width="1460"
|
||||
inkscape:window-height="841"
|
||||
id="namedview3683"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.9721519"
|
||||
inkscape:cx="318.71745"
|
||||
inkscape:cy="345.625"
|
||||
inkscape:window-x="70"
|
||||
inkscape:window-x="68"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g3434"
|
||||
inkscape:current-layer="g3440"
|
||||
inkscape:snap-bbox="false"
|
||||
inkscape:bbox-nodes="false">
|
||||
<inkscape:grid
|
||||
@@ -52,7 +52,7 @@
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
@@ -873,10 +873,10 @@
|
||||
<image
|
||||
sodipodi:absref="C:\Datafiles\Dev\AngularJS\angular.js\images\docs\tutorial\tutorial_10.svg\image10.png"
|
||||
xlink:href="image10.png"
|
||||
id="image3650"
|
||||
height="250"
|
||||
transform="translate(520,28)"
|
||||
width="217"
|
||||
transform="translate(520,28)" />
|
||||
height="250"
|
||||
id="image3650" />
|
||||
</g>
|
||||
<use
|
||||
id="use3646"
|
||||
@@ -1197,13 +1197,13 @@
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4962"
|
||||
y="24.633343"
|
||||
x="162.71861"
|
||||
x="159.5186"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:125%;font-family:'Courier New';-inkscape-font-specification:'Courier New, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
y="24.633343"
|
||||
x="162.71861"
|
||||
id="tspan4964"
|
||||
sodipodi:role="line">phoneList component</tspan></text>
|
||||
sodipodi:role="line"
|
||||
id="tspan3919"
|
||||
x="159.5186"
|
||||
y="24.633343">phoneDetail component</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
id="g5196"
|
||||
@@ -1354,7 +1354,7 @@
|
||||
sodipodi:role="line"
|
||||
id="tspan8160"
|
||||
x="384.30206"
|
||||
y="-28.56875"></tspan></text>
|
||||
y="-28.56875" /></text>
|
||||
</g>
|
||||
<g
|
||||
id="g7990"
|
||||
|
||||
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
@@ -15,7 +15,7 @@
|
||||
id="svg3336"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="tutorial_12.svg"
|
||||
inkscape:export-filename="C:\Datafiles\Dev\AngularJS\angular.js\docs\img\tutorial\tutorial_03.png"
|
||||
inkscape:export-filename="C:\Datafiles\Dev\AngularJS\angular.js\docs\img\tutorial\tutorial_12_2.png"
|
||||
inkscape:export-xdpi="65"
|
||||
inkscape:export-ydpi="65">
|
||||
<sodipodi:namedview
|
||||
@@ -27,14 +27,14 @@
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1458"
|
||||
inkscape:window-width="1460"
|
||||
inkscape:window-height="841"
|
||||
id="namedview3683"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.9721519"
|
||||
inkscape:cx="327.82637"
|
||||
inkscape:cy="345.625"
|
||||
inkscape:window-x="70"
|
||||
inkscape:window-x="68"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g3434"
|
||||
@@ -52,7 +52,7 @@
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
@@ -942,7 +942,7 @@
|
||||
x="0"
|
||||
y="0"
|
||||
style="fill:#ffffff"
|
||||
inkscape:export-filename="C:\Datafiles\Dev\AngularJS\angular.js\docs\img\tutorial\tutorial_12.png"
|
||||
inkscape:export-filename="C:\Datafiles\Dev\AngularJS\angular.js\docs\img\tutorial\tutorial_12_2.png"
|
||||
inkscape:export-xdpi="66"
|
||||
inkscape:export-ydpi="66" />
|
||||
<text
|
||||
@@ -1319,13 +1319,13 @@
|
||||
sodipodi:linespacing="125%"
|
||||
id="text4962"
|
||||
y="24.633343"
|
||||
x="162.71861"
|
||||
x="159.5186"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12px;line-height:125%;font-family:'Courier New';-inkscape-font-specification:'Courier New, Normal';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
xml:space="preserve"><tspan
|
||||
y="24.633343"
|
||||
x="162.71861"
|
||||
id="tspan4964"
|
||||
sodipodi:role="line">phoneList component</tspan></text>
|
||||
sodipodi:role="line"
|
||||
id="tspan3638"
|
||||
x="159.5186"
|
||||
y="24.633343">phoneDetail component</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
id="g5196"
|
||||
@@ -1480,7 +1480,7 @@
|
||||
y="37.03125"
|
||||
x="384.30206"
|
||||
id="tspan8160"
|
||||
sodipodi:role="line"></tspan></text>
|
||||
sodipodi:role="line" /></text>
|
||||
<g
|
||||
id="g7990"
|
||||
transform="translate(0,316.8)">
|
||||
|
||||
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
@@ -1,18 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var angularFiles = require('./angularFiles');
|
||||
var sharedConfig = require('./karma-shared.conf');
|
||||
var karmaConfigFactory = require('./karma-jquery.conf-factory');
|
||||
|
||||
module.exports = function(config) {
|
||||
sharedConfig(config, {testName: 'AngularJS: jQuery', logFile: 'karma-jquery.log'});
|
||||
|
||||
config.set({
|
||||
files: angularFiles.mergeFilesFor('karmaJqueryOld'),
|
||||
exclude: angularFiles.mergeFilesFor('karmaJqueryExclude'),
|
||||
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/jquery.xml',
|
||||
suite: 'jQuery'
|
||||
}
|
||||
});
|
||||
};
|
||||
module.exports = karmaConfigFactory('2.1');
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var karmaConfigFactory = require('./karma-jquery.conf-factory');
|
||||
|
||||
module.exports = karmaConfigFactory('2.2');
|
||||
@@ -0,0 +1,25 @@
|
||||
'use strict';
|
||||
|
||||
var angularFiles = require('./angularFiles');
|
||||
var sharedConfig = require('./karma-shared.conf');
|
||||
|
||||
module.exports = function (version) {
|
||||
version = version || '';
|
||||
|
||||
return function(config) {
|
||||
sharedConfig(config, {
|
||||
testName: 'AngularJS: jQuery' + (version ? ' ' + version : ''),
|
||||
logFile: 'karma-jquery' + version + '.log'
|
||||
});
|
||||
|
||||
config.set({
|
||||
files: angularFiles.mergeFilesFor('karmaJquery' + version),
|
||||
exclude: angularFiles.mergeFilesFor('karmaJqueryExclude'),
|
||||
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/jquery.xml',
|
||||
suite: 'jQuery'
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
@@ -1,18 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
var angularFiles = require('./angularFiles');
|
||||
var sharedConfig = require('./karma-shared.conf');
|
||||
var karmaConfigFactory = require('./karma-jquery.conf-factory');
|
||||
|
||||
module.exports = function(config) {
|
||||
sharedConfig(config, {testName: 'AngularJS: jQuery', logFile: 'karma-jquery.log'});
|
||||
|
||||
config.set({
|
||||
files: angularFiles.mergeFilesFor('karmaJquery'),
|
||||
exclude: angularFiles.mergeFilesFor('karmaJqueryExclude'),
|
||||
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/jquery.xml',
|
||||
suite: 'jQuery'
|
||||
}
|
||||
});
|
||||
};
|
||||
module.exports = karmaConfigFactory();
|
||||
|
||||
@@ -7,7 +7,7 @@ module.exports = function(config) {
|
||||
sharedConfig(config, {testName: 'AngularJS: modules', logFile: 'karma-modules.log'});
|
||||
|
||||
config.set({
|
||||
files: angularFiles.mergeFilesFor('karmaModules', 'angularSrcModules'),
|
||||
files: angularFiles.mergeFilesFor('karmaModules'),
|
||||
|
||||
junitReporter: {
|
||||
outputFile: 'test_out/modules.xml',
|
||||
|
||||
@@ -6,6 +6,11 @@ var shell = require('shelljs');
|
||||
var semver = require('semver');
|
||||
var _ = require('lodash');
|
||||
|
||||
var process = require('process');
|
||||
// We are only interested in whether this environment variable exists, hence the !!
|
||||
var NO_REMOTE_REQUESTS = !!process.env['NG1_BUILD_NO_REMOTE_VERSION_REQUESTS'];
|
||||
var versionSource = NO_REMOTE_REQUESTS ? 'local' : 'remote';
|
||||
|
||||
var currentPackage, previousVersions, cdnVersion, gitRepoInfo;
|
||||
|
||||
|
||||
@@ -96,12 +101,12 @@ var getTaggedVersion = function() {
|
||||
* @return {Array.<SemVer>} The collection of previous versions
|
||||
*/
|
||||
var getPreviousVersions = function() {
|
||||
// always use the remote tags as the local clone might
|
||||
// If we are allowing remote requests then use the remote tags as the local clone might
|
||||
// not contain all commits when cloned with git clone --depth=...
|
||||
// Needed e.g. for Travis
|
||||
// Otherwise just use the tags in the local repository
|
||||
var repo_url = currentPackage.repository.url;
|
||||
var tagResults = shell.exec('git ls-remote --tags ' + repo_url,
|
||||
{silent: true});
|
||||
var query = NO_REMOTE_REQUESTS ? 'git tag' : 'git ls-remote --tags ' + repo_url;
|
||||
var tagResults = shell.exec(query, {silent: true});
|
||||
if (tagResults.code === 0) {
|
||||
return _(tagResults.output.match(/v[0-9].*[0-9]$/mg))
|
||||
.map(function(tag) {
|
||||
@@ -138,15 +143,21 @@ var getCdnVersion = function() {
|
||||
.reverse()
|
||||
.reduce(function(cdnVersion, version) {
|
||||
if (!cdnVersion) {
|
||||
// Note: need to use shell.exec and curl here
|
||||
// as version-infos returns its result synchronously...
|
||||
var cdnResult = shell.exec('curl http://ajax.googleapis.com/ajax/libs/angularjs/' + version + '/angular.min.js ' +
|
||||
'--head --write-out "%{http_code}" -o /dev/null -silent',
|
||||
{silent: true});
|
||||
if (cdnResult.code === 0) {
|
||||
var statusCode = cdnResult.output.trim();
|
||||
if (statusCode === '200') {
|
||||
cdnVersion = version;
|
||||
if (NO_REMOTE_REQUESTS) {
|
||||
// We do not want to make any remote calls to the CDN so just use the most recent version
|
||||
cdnVersion = version;
|
||||
} else {
|
||||
// Note: need to use shell.exec and curl here
|
||||
// as version-infos returns its result synchronously...
|
||||
var cdnResult = shell.exec('curl http://ajax.googleapis.com/ajax/libs/angularjs/' + version + '/angular.min.js ' +
|
||||
'--head --write-out "%{http_code}" -silent',
|
||||
{silent: true});
|
||||
if (cdnResult.code === 0) {
|
||||
// --write-out appends its content to the general request response, so extract it
|
||||
var statusCode = cdnResult.output.split('\n').pop().trim();
|
||||
if (statusCode === '200') {
|
||||
cdnVersion = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -204,3 +215,14 @@ exports.gitRepoInfo = gitRepoInfo = getGitRepoInfo();
|
||||
exports.previousVersions = previousVersions = getPreviousVersions();
|
||||
exports.cdnVersion = cdnVersion = getCdnVersion();
|
||||
exports.currentVersion = getTaggedVersion() || getSnapshotVersion();
|
||||
|
||||
if (NO_REMOTE_REQUESTS) {
|
||||
console.log('==============================================================================================');
|
||||
console.log('Running with no remote requests for version data:');
|
||||
console.log(' - this is due to the "NG1_BUILD_NO_REMOTE_VERSION_REQUESTS" environment variable being defined.');
|
||||
console.log(' - be aware that the generated docs may not have valid or the most recent version information.');
|
||||
console.log('==============================================================================================');
|
||||
}
|
||||
|
||||
console.log('CDN version (' + versionSource + '):', cdnVersion ? cdnVersion.raw : 'No version found.');
|
||||
console.log('Current version (' + versionSource + '):', exports.currentVersion.raw);
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
"version": "3.3.6"
|
||||
},
|
||||
"express": {
|
||||
"version": "4.13.4",
|
||||
"version": "4.14.0",
|
||||
"dependencies": {
|
||||
"accepts": {
|
||||
"version": "1.2.13",
|
||||
"version": "1.3.3",
|
||||
"dependencies": {
|
||||
"mime-types": {
|
||||
"version": "2.1.11",
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
},
|
||||
"negotiator": {
|
||||
"version": "0.5.3"
|
||||
"version": "0.6.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -35,7 +35,7 @@
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"cookie": {
|
||||
"version": "0.1.5"
|
||||
"version": "0.3.1"
|
||||
},
|
||||
"cookie-signature": {
|
||||
"version": "1.0.6"
|
||||
@@ -51,6 +51,9 @@
|
||||
"depd": {
|
||||
"version": "1.1.0"
|
||||
},
|
||||
"encodeurl": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"escape-html": {
|
||||
"version": "1.0.3"
|
||||
},
|
||||
@@ -58,8 +61,11 @@
|
||||
"version": "1.7.0"
|
||||
},
|
||||
"finalhandler": {
|
||||
"version": "0.4.1",
|
||||
"version": "0.5.0",
|
||||
"dependencies": {
|
||||
"statuses": {
|
||||
"version": "1.3.0"
|
||||
},
|
||||
"unpipe": {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
@@ -89,33 +95,36 @@
|
||||
"version": "0.1.7"
|
||||
},
|
||||
"proxy-addr": {
|
||||
"version": "1.0.10",
|
||||
"version": "1.1.2",
|
||||
"dependencies": {
|
||||
"forwarded": {
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"ipaddr.js": {
|
||||
"version": "1.0.5"
|
||||
"version": "1.1.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"qs": {
|
||||
"version": "4.0.0"
|
||||
"version": "6.2.0"
|
||||
},
|
||||
"range-parser": {
|
||||
"version": "1.0.3"
|
||||
"version": "1.2.0"
|
||||
},
|
||||
"send": {
|
||||
"version": "0.13.1",
|
||||
"version": "0.14.1",
|
||||
"dependencies": {
|
||||
"destroy": {
|
||||
"version": "1.0.4"
|
||||
},
|
||||
"http-errors": {
|
||||
"version": "1.3.1",
|
||||
"version": "1.5.0",
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.1"
|
||||
},
|
||||
"setprototypeof": {
|
||||
"version": "1.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -126,15 +135,15 @@
|
||||
"version": "0.7.1"
|
||||
},
|
||||
"statuses": {
|
||||
"version": "1.2.1"
|
||||
"version": "1.3.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve-static": {
|
||||
"version": "1.10.2"
|
||||
"version": "1.11.1"
|
||||
},
|
||||
"type-is": {
|
||||
"version": "1.6.12",
|
||||
"version": "1.6.13",
|
||||
"dependencies": {
|
||||
"media-typer": {
|
||||
"version": "0.3.0"
|
||||
@@ -153,7 +162,7 @@
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"vary": {
|
||||
"version": "1.0.1"
|
||||
"version": "1.1.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -169,16 +178,19 @@
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.5.2",
|
||||
"version": "2.5.3",
|
||||
"dependencies": {
|
||||
"glob": {
|
||||
"version": "7.0.3",
|
||||
"version": "7.0.5",
|
||||
"dependencies": {
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"dependencies": {
|
||||
"wrappy": {
|
||||
"version": "1.0.1"
|
||||
"version": "1.0.2"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -186,10 +198,10 @@
|
||||
"version": "2.0.1"
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.2",
|
||||
"dependencies": {
|
||||
"brace-expansion": {
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.5",
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "0.4.1"
|
||||
@@ -205,7 +217,7 @@
|
||||
"version": "1.3.3",
|
||||
"dependencies": {
|
||||
"wrappy": {
|
||||
"version": "1.0.1"
|
||||
"version": "1.0.2"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -337,7 +349,7 @@
|
||||
}
|
||||
},
|
||||
"browser-resolve": {
|
||||
"version": "1.11.1",
|
||||
"version": "1.11.2",
|
||||
"dependencies": {
|
||||
"resolve": {
|
||||
"version": "1.1.7"
|
||||
@@ -433,13 +445,13 @@
|
||||
"version": "4.0.0",
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "4.11.3"
|
||||
"version": "4.11.4"
|
||||
},
|
||||
"browserify-rsa": {
|
||||
"version": "4.0.1"
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.2.3",
|
||||
"version": "6.3.1",
|
||||
"dependencies": {
|
||||
"brorand": {
|
||||
"version": "1.0.5"
|
||||
@@ -453,7 +465,7 @@
|
||||
"version": "5.0.0",
|
||||
"dependencies": {
|
||||
"asn1.js": {
|
||||
"version": "4.6.0",
|
||||
"version": "4.6.2",
|
||||
"dependencies": {
|
||||
"minimalistic-assert": {
|
||||
"version": "1.0.0"
|
||||
@@ -482,10 +494,10 @@
|
||||
"version": "4.0.0",
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "4.11.3"
|
||||
"version": "4.11.4"
|
||||
},
|
||||
"elliptic": {
|
||||
"version": "6.2.3",
|
||||
"version": "6.3.1",
|
||||
"dependencies": {
|
||||
"brorand": {
|
||||
"version": "1.0.5"
|
||||
@@ -518,7 +530,7 @@
|
||||
"version": "5.0.2",
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "4.11.3"
|
||||
"version": "4.11.4"
|
||||
},
|
||||
"miller-rabin": {
|
||||
"version": "4.0.0",
|
||||
@@ -537,7 +549,7 @@
|
||||
"version": "4.0.0",
|
||||
"dependencies": {
|
||||
"bn.js": {
|
||||
"version": "4.11.3"
|
||||
"version": "4.11.4"
|
||||
},
|
||||
"browserify-rsa": {
|
||||
"version": "4.0.1"
|
||||
@@ -546,7 +558,7 @@
|
||||
"version": "5.0.0",
|
||||
"dependencies": {
|
||||
"asn1.js": {
|
||||
"version": "4.6.0",
|
||||
"version": "4.6.2",
|
||||
"dependencies": {
|
||||
"minimalistic-assert": {
|
||||
"version": "1.0.0"
|
||||
@@ -586,7 +598,7 @@
|
||||
"version": "1.3.9",
|
||||
"dependencies": {
|
||||
"JSONStream": {
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.3",
|
||||
"dependencies": {
|
||||
"jsonparse": {
|
||||
"version": "1.2.0"
|
||||
@@ -611,10 +623,10 @@
|
||||
"version": "4.5.3",
|
||||
"dependencies": {
|
||||
"inflight": {
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"dependencies": {
|
||||
"wrappy": {
|
||||
"version": "1.0.1"
|
||||
"version": "1.0.2"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -622,7 +634,7 @@
|
||||
"version": "2.0.10",
|
||||
"dependencies": {
|
||||
"brace-expansion": {
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.5",
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "0.4.1"
|
||||
@@ -638,7 +650,7 @@
|
||||
"version": "1.3.3",
|
||||
"dependencies": {
|
||||
"wrappy": {
|
||||
"version": "1.0.1"
|
||||
"version": "1.0.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -662,7 +674,7 @@
|
||||
"version": "6.6.3",
|
||||
"dependencies": {
|
||||
"JSONStream": {
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.3",
|
||||
"dependencies": {
|
||||
"jsonparse": {
|
||||
"version": "1.2.0"
|
||||
@@ -711,7 +723,7 @@
|
||||
}
|
||||
},
|
||||
"process": {
|
||||
"version": "0.11.3"
|
||||
"version": "0.11.5"
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.1"
|
||||
@@ -741,7 +753,7 @@
|
||||
"version": "3.9.1",
|
||||
"dependencies": {
|
||||
"JSONStream": {
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.3",
|
||||
"dependencies": {
|
||||
"jsonparse": {
|
||||
"version": "1.2.0"
|
||||
@@ -884,7 +896,7 @@
|
||||
"version": "1.4.2",
|
||||
"dependencies": {
|
||||
"process": {
|
||||
"version": "0.11.3"
|
||||
"version": "0.11.5"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -3128,14 +3140,6 @@
|
||||
},
|
||||
"async-each": {
|
||||
"version": "0.1.6"
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "0.3.8",
|
||||
"dependencies": {
|
||||
"nan": {
|
||||
"version": "2.3.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3943,10 +3947,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"grunt-jasmine-node": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "git://github.com/vojtajina/grunt-jasmine-node.git#ced17cbe52c1412b2ada53160432a5b681f37cd7"
|
||||
},
|
||||
"grunt-jscs": {
|
||||
"version": "2.8.0",
|
||||
"dependencies": {
|
||||
@@ -6463,10 +6463,45 @@
|
||||
}
|
||||
},
|
||||
"jasmine-reporters": {
|
||||
"version": "1.0.2",
|
||||
"version": "2.2.0",
|
||||
"dependencies": {
|
||||
"jasmine": {
|
||||
"version": "2.4.1",
|
||||
"dependencies": {
|
||||
"exit": {
|
||||
"version": "0.1.2"
|
||||
},
|
||||
"glob": {
|
||||
"version": "3.2.11",
|
||||
"dependencies": {
|
||||
"inherits": {
|
||||
"version": "2.0.1"
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "0.3.0",
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "2.7.3"
|
||||
},
|
||||
"sigmund": {
|
||||
"version": "1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.3.5"
|
||||
"version": "0.5.1",
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "0.0.8"
|
||||
}
|
||||
}
|
||||
},
|
||||
"xmldom": {
|
||||
"version": "0.1.22"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6923,398 +6958,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "1.0.12",
|
||||
"dependencies": {
|
||||
"nan": {
|
||||
"version": "2.3.3"
|
||||
},
|
||||
"node-pre-gyp": {
|
||||
"version": "0.6.25",
|
||||
"dependencies": {
|
||||
"nopt": {
|
||||
"version": "3.0.6",
|
||||
"dependencies": {
|
||||
"abbrev": {
|
||||
"version": "1.0.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ansi": {
|
||||
"version": "0.3.1"
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1"
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.2"
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3"
|
||||
},
|
||||
"assert-plus": {
|
||||
"version": "0.2.0"
|
||||
},
|
||||
"async": {
|
||||
"version": "1.5.2"
|
||||
},
|
||||
"aws-sign2": {
|
||||
"version": "0.6.0"
|
||||
},
|
||||
"bl": {
|
||||
"version": "1.0.3"
|
||||
},
|
||||
"block-stream": {
|
||||
"version": "0.0.8"
|
||||
},
|
||||
"boom": {
|
||||
"version": "2.10.1"
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.11.0"
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.5"
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.9.0"
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3"
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"cryptiles": {
|
||||
"version": "2.0.5"
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.2.0"
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.4.1"
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.1"
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5"
|
||||
},
|
||||
"extsprintf": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1"
|
||||
},
|
||||
"fstream": {
|
||||
"version": "1.0.8"
|
||||
},
|
||||
"form-data": {
|
||||
"version": "1.0.0-rc4"
|
||||
},
|
||||
"gauge": {
|
||||
"version": "1.2.7"
|
||||
},
|
||||
"generate-object-property": {
|
||||
"version": "1.2.0"
|
||||
},
|
||||
"generate-function": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.1.3"
|
||||
},
|
||||
"graceful-readlink": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "2.0.6"
|
||||
},
|
||||
"has-ansi": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"hawk": {
|
||||
"version": "3.1.3"
|
||||
},
|
||||
"hoek": {
|
||||
"version": "2.16.3"
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.4"
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.1"
|
||||
},
|
||||
"is-my-json-valid": {
|
||||
"version": "2.13.1"
|
||||
},
|
||||
"is-property": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2"
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"jodid25519": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.2"
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1"
|
||||
},
|
||||
"jsonpointer": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.2.2"
|
||||
},
|
||||
"lodash.pad": {
|
||||
"version": "4.1.0"
|
||||
},
|
||||
"lodash.padstart": {
|
||||
"version": "4.2.0"
|
||||
},
|
||||
"lodash.padend": {
|
||||
"version": "4.2.0"
|
||||
},
|
||||
"lodash.repeat": {
|
||||
"version": "4.0.0"
|
||||
},
|
||||
"lodash.tostring": {
|
||||
"version": "4.1.2"
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.22.0"
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.10"
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1"
|
||||
},
|
||||
"ms": {
|
||||
"version": "0.7.1"
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8"
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "2.0.3"
|
||||
},
|
||||
"node-uuid": {
|
||||
"version": "1.4.7"
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.8.1"
|
||||
},
|
||||
"once": {
|
||||
"version": "1.3.3"
|
||||
},
|
||||
"pinkie": {
|
||||
"version": "2.0.4"
|
||||
},
|
||||
"pinkie-promise": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "1.0.6"
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.0.2"
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.0.6"
|
||||
},
|
||||
"request": {
|
||||
"version": "2.69.0"
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.1.0"
|
||||
},
|
||||
"sntp": {
|
||||
"version": "1.0.9"
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.7.4"
|
||||
},
|
||||
"stringstream": {
|
||||
"version": "0.0.5"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31"
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1"
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "1.0.4"
|
||||
},
|
||||
"tar": {
|
||||
"version": "2.2.1"
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.2.2"
|
||||
},
|
||||
"tar-pack": {
|
||||
"version": "3.1.3"
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.4.2"
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.14.3"
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"verror": {
|
||||
"version": "1.3.6"
|
||||
},
|
||||
"uid-number": {
|
||||
"version": "0.0.6"
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"xtend": {
|
||||
"version": "4.0.1"
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.13.0",
|
||||
"dependencies": {
|
||||
"assert-plus": {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.1.6",
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"aws4": {
|
||||
"version": "1.3.2",
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "4.0.1",
|
||||
"dependencies": {
|
||||
"pseudomap": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"yallist": {
|
||||
"version": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"fstream-ignore": {
|
||||
"version": "1.0.3",
|
||||
"dependencies": {
|
||||
"minimatch": {
|
||||
"version": "3.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": {
|
||||
"version": "1.1.3",
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "0.3.0"
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.5.2",
|
||||
"dependencies": {
|
||||
"glob": {
|
||||
"version": "7.0.3",
|
||||
"dependencies": {
|
||||
"inflight": {
|
||||
"version": "1.0.4",
|
||||
"dependencies": {
|
||||
"wrappy": {
|
||||
"version": "1.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.1"
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": {
|
||||
"version": "1.1.3",
|
||||
"dependencies": {
|
||||
"balanced-match": {
|
||||
"version": "0.3.0"
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.3.3",
|
||||
"dependencies": {
|
||||
"wrappy": {
|
||||
"version": "1.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8792,28 +8435,6 @@
|
||||
},
|
||||
"ultron": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"bufferutil": {
|
||||
"version": "1.2.1",
|
||||
"dependencies": {
|
||||
"bindings": {
|
||||
"version": "1.2.1"
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.3.3"
|
||||
}
|
||||
}
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"version": "1.2.1",
|
||||
"dependencies": {
|
||||
"bindings": {
|
||||
"version": "1.2.1"
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.3.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
"commitizen": "^2.3.0",
|
||||
"cz-conventional-changelog": "1.1.4",
|
||||
"dgeni": "^0.4.0",
|
||||
"dgeni-packages": "^0.13.0",
|
||||
"dgeni-packages": "^0.14.0",
|
||||
"event-stream": "~3.1.0",
|
||||
"glob": "^6.0.1",
|
||||
"grunt": "~0.4.2",
|
||||
@@ -41,7 +41,6 @@
|
||||
"grunt-contrib-copy": "~0.6.0",
|
||||
"grunt-contrib-jshint": "^1.0.0",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
||||
"grunt-jscs": "^2.1.0",
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"grunt-shell": "~1.1.1",
|
||||
@@ -55,7 +54,7 @@
|
||||
"gulp-util": "^3.0.1",
|
||||
"jasmine-core": "^2.4.0",
|
||||
"jasmine-node": "^2.0.0",
|
||||
"jasmine-reporters": "~1.0.1",
|
||||
"jasmine-reporters": "^2.2.0",
|
||||
"jshint": "^2.9.1",
|
||||
"jshint-stylish": "^2.1.0",
|
||||
"karma": "^0.13.19",
|
||||
|
||||
@@ -12,4 +12,6 @@ config.capabilities = {
|
||||
browserName: 'chrome'
|
||||
};
|
||||
|
||||
config.directConnect = true;
|
||||
|
||||
exports.config = config;
|
||||
|
||||
@@ -15,7 +15,7 @@ exports.config = {
|
||||
|
||||
baseUrl: 'http://localhost:8000/',
|
||||
|
||||
framework: 'jasmine',
|
||||
framework: 'jasmine2',
|
||||
|
||||
onPrepare: function() {
|
||||
/* global angular: false, browser: false, jasmine: false */
|
||||
@@ -29,9 +29,10 @@ exports.config = {
|
||||
|
||||
browser.addMockModule('disableNgAnimate', disableNgAnimate);
|
||||
|
||||
require('jasmine-reporters');
|
||||
jasmine.getEnv().addReporter(
|
||||
new jasmine.JUnitXmlReporter('test_out/docs-e2e-' + exports.config.capabilities.browserName + '-', true, true));
|
||||
var reporters = require('jasmine-reporters');
|
||||
jasmine.getEnv().addReporter(new reporters.JUnitXmlReporter({
|
||||
savePath: 'test_out/docs-e2e-' + exports.config.capabilities.browserName + '-'
|
||||
}));
|
||||
},
|
||||
|
||||
jasmineNodeOpts: {
|
||||
|
||||
@@ -5,7 +5,7 @@ exports.config = {
|
||||
|
||||
baseUrl: 'http://localhost:8000/',
|
||||
|
||||
framework: 'jasmine',
|
||||
framework: 'jasmine2',
|
||||
|
||||
onPrepare: function() {
|
||||
/* global angular: false, browser: false, jasmine: false */
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
includes: true,
|
||||
arrayRemove: true,
|
||||
copy: true,
|
||||
shallowCopy: true,
|
||||
equals: true,
|
||||
csp: true,
|
||||
jq: true,
|
||||
@@ -753,7 +752,13 @@ function arrayRemove(array, value) {
|
||||
* * If a destination is provided, all of its elements (for arrays) or properties (for objects)
|
||||
* are deleted and then all elements/properties from the source are copied to it.
|
||||
* * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
|
||||
* * If `source` is identical to 'destination' an exception will be thrown.
|
||||
* * If `source` is identical to `destination` an exception will be thrown.
|
||||
*
|
||||
* <br />
|
||||
* <div class="alert alert-warning">
|
||||
* Only enumerable properties are taken into account. Non-enumerable properties (both on `source`
|
||||
* and on `destination`) will be ignored.
|
||||
* </div>
|
||||
*
|
||||
* @param {*} source The source that will be used to make a copy.
|
||||
* Can be any type, including primitives, `null`, and `undefined`.
|
||||
@@ -762,41 +767,42 @@ function arrayRemove(array, value) {
|
||||
* @returns {*} The copy or updated `destination`, if `destination` was specified.
|
||||
*
|
||||
* @example
|
||||
<example module="copyExample">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
<form novalidate class="simple-form">
|
||||
Name: <input type="text" ng-model="user.name" /><br />
|
||||
E-mail: <input type="email" ng-model="user.email" /><br />
|
||||
Gender: <input type="radio" ng-model="user.gender" value="male" />male
|
||||
<input type="radio" ng-model="user.gender" value="female" />female<br />
|
||||
<button ng-click="reset()">RESET</button>
|
||||
<button ng-click="update(user)">SAVE</button>
|
||||
</form>
|
||||
<pre>form = {{user | json}}</pre>
|
||||
<pre>master = {{master | json}}</pre>
|
||||
</div>
|
||||
<example module="copyExample">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
<form novalidate class="simple-form">
|
||||
<label>Name: <input type="text" ng-model="user.name" /></label><br />
|
||||
<label>Age: <input type="number" ng-model="user.age" /></label><br />
|
||||
Gender: <label><input type="radio" ng-model="user.gender" value="male" />male</label>
|
||||
<label><input type="radio" ng-model="user.gender" value="female" />female</label><br />
|
||||
<button ng-click="reset()">RESET</button>
|
||||
<button ng-click="update(user)">SAVE</button>
|
||||
</form>
|
||||
<pre>form = {{user | json}}</pre>
|
||||
<pre>master = {{master | json}}</pre>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
// Module: copyExample
|
||||
angular.
|
||||
module('copyExample', []).
|
||||
controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.master = {};
|
||||
|
||||
<script>
|
||||
angular.module('copyExample', [])
|
||||
.controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.master= {};
|
||||
$scope.reset = function() {
|
||||
// Example with 1 argument
|
||||
$scope.user = angular.copy($scope.master);
|
||||
};
|
||||
|
||||
$scope.update = function(user) {
|
||||
// Example with 1 argument
|
||||
$scope.master= angular.copy(user);
|
||||
};
|
||||
$scope.update = function(user) {
|
||||
// Example with 2 arguments
|
||||
angular.copy(user, $scope.master);
|
||||
};
|
||||
|
||||
$scope.reset = function() {
|
||||
// Example with 2 arguments
|
||||
angular.copy($scope.master, $scope.user);
|
||||
};
|
||||
|
||||
$scope.reset();
|
||||
}]);
|
||||
</script>
|
||||
</file>
|
||||
</example>
|
||||
$scope.reset();
|
||||
}]);
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
function copy(source, destination) {
|
||||
var stackSource = [];
|
||||
@@ -903,7 +909,7 @@ function copy(source, destination) {
|
||||
case '[object Uint8ClampedArray]':
|
||||
case '[object Uint16Array]':
|
||||
case '[object Uint32Array]':
|
||||
return new source.constructor(copyElement(source.buffer));
|
||||
return new source.constructor(copyElement(source.buffer), source.byteOffset, source.length);
|
||||
|
||||
case '[object ArrayBuffer]':
|
||||
//Support: IE10
|
||||
@@ -935,31 +941,6 @@ function copy(source, destination) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a shallow copy of an object, an array or a primitive.
|
||||
*
|
||||
* Assumes that there are no proto properties for objects.
|
||||
*/
|
||||
function shallowCopy(src, dst) {
|
||||
if (isArray(src)) {
|
||||
dst = dst || [];
|
||||
|
||||
for (var i = 0, ii = src.length; i < ii; i++) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
} else if (isObject(src)) {
|
||||
dst = dst || {};
|
||||
|
||||
for (var key in src) {
|
||||
if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dst || src;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
$HttpParamSerializerJQLikeProvider,
|
||||
$HttpBackendProvider,
|
||||
$xhrFactoryProvider,
|
||||
$jsonpCallbacksProvider,
|
||||
$LocationProvider,
|
||||
$LogProvider,
|
||||
$ParseProvider,
|
||||
@@ -148,7 +149,7 @@ function publishExternalAPI(angular) {
|
||||
'isDate': isDate,
|
||||
'lowercase': lowercase,
|
||||
'uppercase': uppercase,
|
||||
'callbacks': {counter: 0},
|
||||
'callbacks': {$$counter: 0},
|
||||
'getTestability': getTestability,
|
||||
'$$minErr': minErr,
|
||||
'$$csp': csp,
|
||||
@@ -237,6 +238,7 @@ function publishExternalAPI(angular) {
|
||||
$httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
|
||||
$httpBackend: $HttpBackendProvider,
|
||||
$xhrFactory: $xhrFactoryProvider,
|
||||
$jsonpCallbacks: $jsonpCallbacksProvider,
|
||||
$location: $LocationProvider,
|
||||
$log: $LogProvider,
|
||||
$parse: $ParseProvider,
|
||||
|
||||
@@ -853,10 +853,10 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
if (msie <= 11) {
|
||||
return false;
|
||||
}
|
||||
// Workaround for MS Edge.
|
||||
// Check https://connect.microsoft.com/IE/Feedback/Details/2211653
|
||||
// Support: Edge 12-13 only
|
||||
// See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6156135/
|
||||
return typeof func === 'function'
|
||||
&& /^(?:class\s|constructor\()/.test(stringifyFn(func));
|
||||
&& /^(?:class\b|constructor\()/.test(stringifyFn(func));
|
||||
}
|
||||
|
||||
function invoke(fn, self, locals, serviceName) {
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
* ## Angular's jqLite
|
||||
* jqLite provides only the following jQuery methods:
|
||||
*
|
||||
* - [`addClass()`](http://api.jquery.com/addClass/)
|
||||
* - [`addClass()`](http://api.jquery.com/addClass/) - Does not support a function as first argument
|
||||
* - [`after()`](http://api.jquery.com/after/)
|
||||
* - [`append()`](http://api.jquery.com/append/)
|
||||
* - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
|
||||
@@ -80,7 +80,7 @@
|
||||
* - [`ready()`](http://api.jquery.com/ready/)
|
||||
* - [`remove()`](http://api.jquery.com/remove/)
|
||||
* - [`removeAttr()`](http://api.jquery.com/removeAttr/)
|
||||
* - [`removeClass()`](http://api.jquery.com/removeClass/)
|
||||
* - [`removeClass()`](http://api.jquery.com/removeClass/) - Does not support a function as first argument
|
||||
* - [`removeData()`](http://api.jquery.com/removeData/)
|
||||
* - [`replaceWith()`](http://api.jquery.com/replaceWith/)
|
||||
* - [`text()`](http://api.jquery.com/text/)
|
||||
@@ -215,7 +215,7 @@ function jqLiteBuildFragment(html, context) {
|
||||
nodes.push(context.createTextNode(html));
|
||||
} else {
|
||||
// Convert html into DOM nodes
|
||||
tmp = tmp || fragment.appendChild(context.createElement("div"));
|
||||
tmp = fragment.appendChild(context.createElement("div"));
|
||||
tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
|
||||
wrap = wrapMap[tag] || wrapMap._default;
|
||||
tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
|
||||
|
||||
@@ -420,7 +420,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* @param {DOMElement} parent the parent element which will append the element as
|
||||
* a child (so long as the after element is not present)
|
||||
* @param {DOMElement=} after the sibling element after which the element will be appended
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
@@ -446,7 +452,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* @param {DOMElement} parent the parent element which will append the element as
|
||||
* a child (so long as the after element is not present)
|
||||
* @param {DOMElement=} after the sibling element after which the element will be appended
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
@@ -467,7 +479,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* digest once the animation has completed.
|
||||
*
|
||||
* @param {DOMElement} element the element which will be removed from the DOM
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
@@ -491,7 +509,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
*
|
||||
* @param {DOMElement} element the element which the CSS classes will be applied to
|
||||
* @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
@@ -515,7 +539,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
*
|
||||
* @param {DOMElement} element the element which the CSS classes will be applied to
|
||||
* @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
@@ -540,7 +570,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* @param {DOMElement} element the element which the CSS classes will be applied to
|
||||
* @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
|
||||
* @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
@@ -581,7 +617,13 @@ var $AnimateProvider = ['$provide', function($provide) {
|
||||
* @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
|
||||
* this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
|
||||
* (Note that if no animation is detected then this value will not be applied to the element.)
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element
|
||||
* @param {object=} options an optional collection of options/styles that will be applied to the element.
|
||||
* The object can have the following properties:
|
||||
*
|
||||
* - **addClass** - `{string}` - space-separated CSS classes to add to element
|
||||
* - **from** - `{Object}` - CSS properties & values at the beginning of animation. Must have matching `to`
|
||||
* - **removeClass** - `{string}` - space-separated CSS classes to remove from element
|
||||
* - **to** - `{Object}` - CSS properties & values at end of animation. Must have matching `from`
|
||||
*
|
||||
* @return {Promise} the animation callback promise
|
||||
*/
|
||||
|
||||
@@ -52,8 +52,9 @@
|
||||
* There are many different options for a directive.
|
||||
*
|
||||
* The difference resides in the return value of the factory function.
|
||||
* You can either return a "Directive Definition Object" (see below) that defines the directive properties,
|
||||
* or just the `postLink` function (all other properties will have the default values).
|
||||
* You can either return a {@link $compile#directive-definition-object Directive Definition Object (see below)}
|
||||
* that defines the directive properties, or just the `postLink` function (all other properties will have
|
||||
* the default values).
|
||||
*
|
||||
* <div class="alert alert-success">
|
||||
* **Best Practice:** It's recommended to use the "directive definition object" form.
|
||||
@@ -117,6 +118,125 @@
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* ### Life-cycle hooks
|
||||
* Directive controllers can provide the following methods that are called by Angular at points in the life-cycle of the
|
||||
* directive:
|
||||
* * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
|
||||
* had their bindings initialized (and before the pre & post linking functions for the directives on
|
||||
* this element). This is a good place to put initialization code for your controller.
|
||||
* * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
|
||||
* `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
|
||||
* object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
|
||||
* component such as cloning the bound value to prevent accidental mutation of the outer value.
|
||||
* * `$doCheck()` - Called on each turn of the digest cycle. Provides an opportunity to detect and act on
|
||||
* changes. Any actions that you wish to take in response to the changes that you detect must be
|
||||
* invoked from this hook; implementing this has no effect on when `$onChanges` is called. For example, this hook
|
||||
* could be useful if you wish to perform a deep equality check, or to check a Date object, changes to which would not
|
||||
* be detected by Angular's change detector and thus not trigger `$onChanges`. This hook is invoked with no arguments;
|
||||
* if detecting changes, you must store the previous value(s) for comparison to the current values.
|
||||
* * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
|
||||
* external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
|
||||
* the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
|
||||
* components will have their `$onDestroy()` hook called before child components.
|
||||
* * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
|
||||
* function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
|
||||
* Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
|
||||
* they are waiting for their template to load asynchronously and their own compilation and linking has been
|
||||
* suspended until that occurs.
|
||||
*
|
||||
* #### Comparison with Angular 2 life-cycle hooks
|
||||
* Angular 2 also uses life-cycle hooks for its components. While the Angular 1 life-cycle hooks are similar there are
|
||||
* some differences that you should be aware of, especially when it comes to moving your code from Angular 1 to Angular 2:
|
||||
*
|
||||
* * Angular 1 hooks are prefixed with `$`, such as `$onInit`. Angular 2 hooks are prefixed with `ng`, such as `ngOnInit`.
|
||||
* * Angular 1 hooks can be defined on the controller prototype or added to the controller inside its constructor.
|
||||
* In Angular 2 you can only define hooks on the prototype of the Component class.
|
||||
* * Due to the differences in change-detection, you may get many more calls to `$doCheck` in Angular 1 than you would to
|
||||
* `ngDoCheck` in Angular 2
|
||||
* * Changes to the model inside `$doCheck` will trigger new turns of the digest loop, which will cause the changes to be
|
||||
* propagated throughout the application.
|
||||
* Angular 2 does not allow the `ngDoCheck` hook to trigger a change outside of the component. It will either throw an
|
||||
* error or do nothing depending upon the state of `enableProdMode()`.
|
||||
*
|
||||
* #### Life-cycle hook examples
|
||||
*
|
||||
* This example shows how you can check for mutations to a Date object even though the identity of the object
|
||||
* has not changed.
|
||||
*
|
||||
* <example name="doCheckDateExample" module="do-check-module">
|
||||
* <file name="app.js">
|
||||
* angular.module('do-check-module', [])
|
||||
* .component('app', {
|
||||
* template:
|
||||
* 'Month: <input ng-model="$ctrl.month" ng-change="$ctrl.updateDate()">' +
|
||||
* 'Date: {{ $ctrl.date }}' +
|
||||
* '<test date="$ctrl.date"></test>',
|
||||
* controller: function() {
|
||||
* this.date = new Date();
|
||||
* this.month = this.date.getMonth();
|
||||
* this.updateDate = function() {
|
||||
* this.date.setMonth(this.month);
|
||||
* };
|
||||
* }
|
||||
* })
|
||||
* .component('test', {
|
||||
* bindings: { date: '<' },
|
||||
* template:
|
||||
* '<pre>{{ $ctrl.log | json }}</pre>',
|
||||
* controller: function() {
|
||||
* var previousValue;
|
||||
* this.log = [];
|
||||
* this.$doCheck = function() {
|
||||
* var currentValue = this.date && this.date.valueOf();
|
||||
* if (previousValue !== currentValue) {
|
||||
* this.log.push('doCheck: date mutated: ' + this.date);
|
||||
* previousValue = currentValue;
|
||||
* }
|
||||
* };
|
||||
* }
|
||||
* });
|
||||
* </file>
|
||||
* <file name="index.html">
|
||||
* <app></app>
|
||||
* </file>
|
||||
* </example>
|
||||
*
|
||||
* This example show how you might use `$doCheck` to trigger changes in your component's inputs even if the
|
||||
* actual identity of the component doesn't change. (Be aware that cloning and deep equality checks on large
|
||||
* arrays or objects can have a negative impact on your application performance)
|
||||
*
|
||||
* <example name="doCheckArrayExample" module="do-check-module">
|
||||
* <file name="index.html">
|
||||
* <div ng-init="items = []">
|
||||
* <button ng-click="items.push(items.length)">Add Item</button>
|
||||
* <button ng-click="items = []">Reset Items</button>
|
||||
* <pre>{{ items }}</pre>
|
||||
* <test items="items"></test>
|
||||
* </div>
|
||||
* </file>
|
||||
* <file name="app.js">
|
||||
* angular.module('do-check-module', [])
|
||||
* .component('test', {
|
||||
* bindings: { items: '<' },
|
||||
* template:
|
||||
* '<pre>{{ $ctrl.log | json }}</pre>',
|
||||
* controller: function() {
|
||||
* this.log = [];
|
||||
*
|
||||
* this.$doCheck = function() {
|
||||
* if (this.items_ref !== this.items) {
|
||||
* this.log.push('doCheck: items changed');
|
||||
* this.items_ref = this.items;
|
||||
* }
|
||||
* if (!angular.equals(this.items_clone, this.items)) {
|
||||
* this.log.push('doCheck: items mutated');
|
||||
* this.items_clone = angular.copy(this.items);
|
||||
* }
|
||||
* };
|
||||
* }
|
||||
* });
|
||||
* </file>
|
||||
* </example>
|
||||
*
|
||||
*
|
||||
* ### Directive Definition Object
|
||||
@@ -292,25 +412,6 @@
|
||||
* The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
|
||||
* `true` if the specified slot contains content (i.e. one or more DOM nodes).
|
||||
*
|
||||
* The controller can provide the following methods that act as life-cycle hooks:
|
||||
* * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
|
||||
* had their bindings initialized (and before the pre & post linking functions for the directives on
|
||||
* this element). This is a good place to put initialization code for your controller.
|
||||
* * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
|
||||
* `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
|
||||
* object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
|
||||
* component such as cloning the bound value to prevent accidental mutation of the outer value.
|
||||
* * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
|
||||
* external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
|
||||
* the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
|
||||
* components will have their `$onDestroy()` hook called before child components.
|
||||
* * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
|
||||
* function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
|
||||
* Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
|
||||
* they are waiting for their template to load asynchronously and their own compilation and linking has been
|
||||
* suspended until that occurs.
|
||||
*
|
||||
*
|
||||
* #### `require`
|
||||
* Require another directive and inject its controller as the fourth argument to the linking function. The
|
||||
* `require` property can be a string, an array or an object:
|
||||
@@ -508,8 +609,8 @@
|
||||
* any other controller.
|
||||
*
|
||||
* * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
|
||||
* This is the same as the `$transclude`
|
||||
* parameter of directive controllers, see there for details.
|
||||
* This is the same as the `$transclude` parameter of directive controllers,
|
||||
* see {@link ng.$compile#-controller- the controller section for details}.
|
||||
* `function([scope], cloneLinkingFn, futureParentElement)`.
|
||||
*
|
||||
* #### Pre-linking function
|
||||
@@ -1311,11 +1412,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
// We must run this hook in an apply since the $$postDigest runs outside apply
|
||||
$rootScope.$apply(function() {
|
||||
var errors = [];
|
||||
for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
|
||||
onChangesQueue[i]();
|
||||
try {
|
||||
onChangesQueue[i]();
|
||||
} catch (e) {
|
||||
errors.push(e);
|
||||
}
|
||||
}
|
||||
// Reset the queue to trigger a new schedule next time there is a change
|
||||
onChangesQueue = undefined;
|
||||
if (errors.length) {
|
||||
throw errors;
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
onChangesTtl++;
|
||||
@@ -1956,19 +2065,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
addTextInterpolateDirective(directives, node.nodeValue);
|
||||
break;
|
||||
case NODE_TYPE_COMMENT: /* Comment */
|
||||
try {
|
||||
match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
|
||||
if (match) {
|
||||
nName = directiveNormalize(match[1]);
|
||||
if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
|
||||
attrs[nName] = trim(match[2]);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// turns out that under some circumstances IE9 throws errors when one attempts to read
|
||||
// comment's node value.
|
||||
// Just ignore it and continue. (Can't seem to reproduce in test case.)
|
||||
}
|
||||
collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1976,6 +2073,24 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
return directives;
|
||||
}
|
||||
|
||||
function collectCommentDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
|
||||
// function created because of performance, try/catch disables
|
||||
// the optimization of the whole function #14848
|
||||
try {
|
||||
var match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
|
||||
if (match) {
|
||||
var nName = directiveNormalize(match[1]);
|
||||
if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
|
||||
attrs[nName] = trim(match[2]);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// turns out that under some circumstances IE9 throws errors when one attempts to read
|
||||
// comment's node value.
|
||||
// Just ignore it and continue. (Can't seem to reproduce in test case.)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a node with an directive-start it collects all of the siblings until it finds
|
||||
* directive-end.
|
||||
@@ -2491,10 +2606,22 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
forEach(elementControllers, function(controller) {
|
||||
var controllerInstance = controller.instance;
|
||||
if (isFunction(controllerInstance.$onChanges)) {
|
||||
controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
|
||||
try {
|
||||
controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
}
|
||||
if (isFunction(controllerInstance.$onInit)) {
|
||||
controllerInstance.$onInit();
|
||||
try {
|
||||
controllerInstance.$onInit();
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
}
|
||||
if (isFunction(controllerInstance.$doCheck)) {
|
||||
controllerScope.$watch(function() { controllerInstance.$doCheck(); });
|
||||
controllerInstance.$doCheck();
|
||||
}
|
||||
if (isFunction(controllerInstance.$onDestroy)) {
|
||||
controllerScope.$on('$destroy', function callOnDestroyHook() {
|
||||
@@ -2758,18 +2885,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
// copy the new attributes on the old attrs object
|
||||
forEach(src, function(value, key) {
|
||||
if (key == 'class') {
|
||||
safeAddClass($element, value);
|
||||
dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
|
||||
} else if (key == 'style') {
|
||||
$element.attr('style', $element.attr('style') + ';' + value);
|
||||
dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
|
||||
// `dst` will never contain hasOwnProperty as DOM parser won't let it.
|
||||
// You will get an "InvalidCharacterError: DOM Exception 5" error if you
|
||||
// have an attribute like "has-own-property" or "data-has-own-property", etc.
|
||||
} else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
|
||||
// Check if we already set this attribute in the loop above.
|
||||
// `dst` will never contain hasOwnProperty as DOM parser won't let it.
|
||||
// You will get an "InvalidCharacterError: DOM Exception 5" error if you
|
||||
// have an attribute like "has-own-property" or "data-has-own-property", etc.
|
||||
if (!dst.hasOwnProperty(key) && key.charAt(0) !== '$') {
|
||||
dst[key] = value;
|
||||
dstAttr[key] = srcAttr[key];
|
||||
|
||||
if (key !== 'class' && key !== 'style') {
|
||||
dstAttr[key] = srcAttr[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -3144,7 +3269,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
forEach(bindings, function initializeBinding(definition, scopeName) {
|
||||
var attrName = definition.attrName,
|
||||
optional = definition.optional,
|
||||
mode = definition.mode, // @, =, or &
|
||||
mode = definition.mode, // @, =, <, or &
|
||||
lastValue,
|
||||
parentGet, parentSet, compare, removeWatch;
|
||||
|
||||
|
||||
@@ -230,9 +230,11 @@
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
|
||||
* Sets the `readonly` attribute on the element, if the expression inside `ngReadonly` is truthy.
|
||||
* Note that `readonly` applies only to `input` elements with specific types. [See the input docs on
|
||||
* MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-readonly) for more information.
|
||||
*
|
||||
* A special directive is necessary because we cannot use interpolation inside the `readOnly`
|
||||
* A special directive is necessary because we cannot use interpolation inside the `readonly`
|
||||
* attribute. See the {@link guide/interpolation interpolation guide} for more info.
|
||||
*
|
||||
* @example
|
||||
@@ -269,6 +271,13 @@
|
||||
* A special directive is necessary because we cannot use interpolation inside the `selected`
|
||||
* attribute. See the {@link guide/interpolation interpolation guide} for more info.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** `ngSelected` does not interact with the `select` and `ngModel` directives, it only
|
||||
* sets the `selected` attribute on the element. If you are using `ngModel` on the select, you
|
||||
* should not use `ngSelected` on the options, as `ngModel` will set the select value and
|
||||
* selected options.
|
||||
* </div>
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
<file name="index.html">
|
||||
@@ -305,6 +314,11 @@
|
||||
* A special directive is necessary because we cannot use interpolation inside the `open`
|
||||
* attribute. See the {@link guide/interpolation interpolation guide} for more info.
|
||||
*
|
||||
* ## A note about browser compatibility
|
||||
*
|
||||
* Edge, Firefox, and Internet Explorer do not support the `details` element, it is
|
||||
* recommended to use {@link ng.ngShow} and {@link ng.ngHide} instead.
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
<file name="index.html">
|
||||
|
||||
@@ -24,7 +24,9 @@ var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-
|
||||
// 9. Fragment
|
||||
// 1111111111111111 222 333333 44444 555555555555555555555555 666 77777777 8888888 999
|
||||
var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
|
||||
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
|
||||
/* jshint maxlen:220 */
|
||||
var EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+\/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
|
||||
/* jshint maxlen:200 */
|
||||
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
|
||||
var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
|
||||
var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
|
||||
@@ -1409,7 +1411,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
|
||||
attr.$observe('min', function(val) {
|
||||
if (isDefined(val) && !isNumber(val)) {
|
||||
val = parseFloat(val, 10);
|
||||
val = parseFloat(val);
|
||||
}
|
||||
minVal = isNumber(val) && !isNaN(val) ? val : undefined;
|
||||
// TODO(matsko): implement validateLater to reduce number of validations
|
||||
@@ -1425,7 +1427,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
|
||||
attr.$observe('max', function(val) {
|
||||
if (isDefined(val) && !isNumber(val)) {
|
||||
val = parseFloat(val, 10);
|
||||
val = parseFloat(val);
|
||||
}
|
||||
maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
|
||||
// TODO(matsko): implement validateLater to reduce number of validations
|
||||
|
||||
@@ -153,6 +153,11 @@ function classDirective(name, selector) {
|
||||
* When the expression changes, the previously added classes are removed and only then are the
|
||||
* new classes added.
|
||||
*
|
||||
* @knownIssue
|
||||
* You should not use {@link guide/interpolation interpolation} in the value of the `class`
|
||||
* attribute, when using the `ngClass` directive on the same element.
|
||||
* See {@link guide/interpolation#known-issues here} for more info.
|
||||
*
|
||||
* @animations
|
||||
* | Animation | Occurs |
|
||||
* |----------------------------------|-------------------------------------|
|
||||
|
||||
@@ -629,7 +629,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
|
||||
for (var i = options.items.length - 1; i >= 0; i--) {
|
||||
var option = options.items[i];
|
||||
if (option.group) {
|
||||
if (isDefined(option.group)) {
|
||||
jqLiteRemove(option.element.parentNode);
|
||||
} else {
|
||||
jqLiteRemove(option.element);
|
||||
@@ -661,7 +661,8 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
|
||||
listFragment.appendChild(groupElement);
|
||||
|
||||
// Update the label on the group element
|
||||
groupElement.label = option.group;
|
||||
// "null" is special cased because of Safari
|
||||
groupElement.label = option.group === null ? 'null' : option.group;
|
||||
|
||||
// Store it for use later
|
||||
groupElementMap[option.group] = groupElement;
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
* it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
|
||||
*
|
||||
* - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
|
||||
* objects, and will throw if used with one.
|
||||
* objects, and will throw an error if used with one.
|
||||
*
|
||||
* If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
|
||||
* that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
|
||||
|
||||
@@ -8,6 +8,11 @@
|
||||
* @description
|
||||
* The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
|
||||
*
|
||||
* @knownIssue
|
||||
* You should not use {@link guide/interpolation interpolation} in the value of the `style`
|
||||
* attribute, when using the `ngStyle` directive on the same element.
|
||||
* See {@link guide/interpolation#known-issues here} for more info.
|
||||
*
|
||||
* @element ANY
|
||||
* @param {expression} ngStyle
|
||||
*
|
||||
|
||||
@@ -159,35 +159,60 @@
|
||||
* </example>
|
||||
*/
|
||||
var ngTranscludeMinErr = minErr('ngTransclude');
|
||||
var ngTranscludeDirective = ngDirective({
|
||||
restrict: 'EAC',
|
||||
link: function($scope, $element, $attrs, controller, $transclude) {
|
||||
var ngTranscludeDirective = ['$compile', function($compile) {
|
||||
return {
|
||||
restrict: 'EAC',
|
||||
terminal: true,
|
||||
compile: function ngTranscludeCompile(tElement) {
|
||||
|
||||
if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
|
||||
// If the attribute is of the form: `ng-transclude="ng-transclude"`
|
||||
// then treat it like the default
|
||||
$attrs.ngTransclude = '';
|
||||
// Remove and cache any original content to act as a fallback
|
||||
var fallbackLinkFn = $compile(tElement.contents());
|
||||
tElement.empty();
|
||||
|
||||
return function ngTranscludePostLink($scope, $element, $attrs, controller, $transclude) {
|
||||
|
||||
if (!$transclude) {
|
||||
throw ngTranscludeMinErr('orphan',
|
||||
'Illegal use of ngTransclude directive in the template! ' +
|
||||
'No parent directive that requires a transclusion found. ' +
|
||||
'Element: {0}',
|
||||
startingTag($element));
|
||||
}
|
||||
|
||||
|
||||
// If the attribute is of the form: `ng-transclude="ng-transclude"` then treat it like the default
|
||||
if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
|
||||
$attrs.ngTransclude = '';
|
||||
}
|
||||
var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
|
||||
|
||||
// If the slot is required and no transclusion content is provided then this call will throw an error
|
||||
$transclude(ngTranscludeCloneAttachFn, null, slotName);
|
||||
|
||||
// If the slot is optional and no transclusion content is provided then use the fallback content
|
||||
if (slotName && !$transclude.isSlotFilled(slotName)) {
|
||||
useFallbackContent();
|
||||
}
|
||||
|
||||
function ngTranscludeCloneAttachFn(clone, transcludedScope) {
|
||||
if (clone.length) {
|
||||
$element.append(clone);
|
||||
} else {
|
||||
useFallbackContent();
|
||||
// There is nothing linked against the transcluded scope since no content was available,
|
||||
// so it should be safe to clean up the generated scope.
|
||||
transcludedScope.$destroy();
|
||||
}
|
||||
}
|
||||
|
||||
function useFallbackContent() {
|
||||
// Since this is the fallback content rather than the transcluded content,
|
||||
// we link against the scope of this directive rather than the transcluded scope
|
||||
fallbackLinkFn($scope, function(clone) {
|
||||
$element.append(clone);
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function ngTranscludeCloneAttachFn(clone) {
|
||||
if (clone.length) {
|
||||
$element.empty();
|
||||
$element.append(clone);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$transclude) {
|
||||
throw ngTranscludeMinErr('orphan',
|
||||
'Illegal use of ngTransclude directive in the template! ' +
|
||||
'No parent directive that requires a transclusion found. ' +
|
||||
'Element: {0}',
|
||||
startingTag($element));
|
||||
}
|
||||
|
||||
// If there is no slot name defined or the slot name is not optional
|
||||
// then transclude the slot
|
||||
var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
|
||||
$transclude(ngTranscludeCloneAttachFn, null, slotName);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
}];
|
||||
|
||||
@@ -15,17 +15,20 @@
|
||||
*
|
||||
* ## Example:
|
||||
*
|
||||
* ```js
|
||||
* angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
|
||||
* return function(exception, cause) {
|
||||
* exception.message += ' (caused by "' + cause + '")';
|
||||
* throw exception;
|
||||
* };
|
||||
* });
|
||||
* ```
|
||||
* The example below will overwrite the default `$exceptionHandler` in order to (a) log uncaught
|
||||
* errors to the backend for later inspection by the developers and (b) to use `$log.warn()` instead
|
||||
* of `$log.error()`.
|
||||
*
|
||||
* This example will override the normal action of `$exceptionHandler`, to make angular
|
||||
* exceptions fail hard when they happen, instead of just logging to the console.
|
||||
* ```js
|
||||
* angular.
|
||||
* module('exceptionOverwrite', []).
|
||||
* factory('$exceptionHandler', ['$log', 'logErrorsToBackend', function($log, logErrorsToBackend) {
|
||||
* return function myExceptionHandler(exception, cause) {
|
||||
* logErrorsToBackend(exception, cause);
|
||||
* $log.warn(exception, cause);
|
||||
* };
|
||||
* }]);
|
||||
* ```
|
||||
*
|
||||
* <hr />
|
||||
* Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
|
||||
@@ -36,7 +39,7 @@
|
||||
* `try { ... } catch(e) { $exceptionHandler(e); }`
|
||||
*
|
||||
* @param {Error} exception Exception associated with the error.
|
||||
* @param {string=} cause optional information about the context in which
|
||||
* @param {string=} cause Optional information about the context in which
|
||||
* the error was thrown.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -22,10 +22,11 @@
|
||||
* - `Object`: A pattern object can be used to filter specific properties on objects contained
|
||||
* by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
|
||||
* which have property `name` containing "M" and property `phone` containing "1". A special
|
||||
* property name `$` can be used (as in `{$:"text"}`) to accept a match against any
|
||||
* property of the object or its nested object properties. That's equivalent to the simple
|
||||
* substring match with a `string` as described above. The predicate can be negated by prefixing
|
||||
* the string with `!`.
|
||||
* property name (`$` by default) can be used (e.g. as in `{$: "text"}`) to accept a match
|
||||
* against any property of the object or its nested object properties. That's equivalent to the
|
||||
* simple substring match with a `string` as described above. The special property name can be
|
||||
* overwritten, using the `anyPropertyKey` parameter.
|
||||
* The predicate can be negated by prefixing the string with `!`.
|
||||
* For example `{name: "!M"}` predicate will return an array of items which have property `name`
|
||||
* not containing "M".
|
||||
*
|
||||
@@ -59,6 +60,9 @@
|
||||
* Primitive values are converted to strings. Objects are not compared against primitives,
|
||||
* unless they have a custom `toString` method (e.g. `Date` objects).
|
||||
*
|
||||
* @param {string=} anyPropertyKey The special property name that matches against any property.
|
||||
* By default `$`.
|
||||
*
|
||||
* @example
|
||||
<example>
|
||||
<file name="index.html">
|
||||
@@ -127,8 +131,9 @@
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
|
||||
function filterFilter() {
|
||||
return function(array, expression, comparator) {
|
||||
return function(array, expression, comparator, anyPropertyKey) {
|
||||
if (!isArrayLike(array)) {
|
||||
if (array == null) {
|
||||
return array;
|
||||
@@ -137,6 +142,7 @@ function filterFilter() {
|
||||
}
|
||||
}
|
||||
|
||||
anyPropertyKey = anyPropertyKey || '$';
|
||||
var expressionType = getTypeForFilter(expression);
|
||||
var predicateFn;
|
||||
var matchAgainstAnyProp;
|
||||
@@ -153,7 +159,7 @@ function filterFilter() {
|
||||
//jshint -W086
|
||||
case 'object':
|
||||
//jshint +W086
|
||||
predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
|
||||
predicateFn = createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp);
|
||||
break;
|
||||
default:
|
||||
return array;
|
||||
@@ -164,8 +170,8 @@ function filterFilter() {
|
||||
}
|
||||
|
||||
// Helper functions for `filterFilter`
|
||||
function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
|
||||
var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
|
||||
function createPredicateFn(expression, comparator, anyPropertyKey, matchAgainstAnyProp) {
|
||||
var shouldMatchPrimitives = isObject(expression) && (anyPropertyKey in expression);
|
||||
var predicateFn;
|
||||
|
||||
if (comparator === true) {
|
||||
@@ -193,25 +199,25 @@ function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
|
||||
|
||||
predicateFn = function(item) {
|
||||
if (shouldMatchPrimitives && !isObject(item)) {
|
||||
return deepCompare(item, expression.$, comparator, false);
|
||||
return deepCompare(item, expression[anyPropertyKey], comparator, anyPropertyKey, false);
|
||||
}
|
||||
return deepCompare(item, expression, comparator, matchAgainstAnyProp);
|
||||
return deepCompare(item, expression, comparator, anyPropertyKey, matchAgainstAnyProp);
|
||||
};
|
||||
|
||||
return predicateFn;
|
||||
}
|
||||
|
||||
function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
|
||||
function deepCompare(actual, expected, comparator, anyPropertyKey, matchAgainstAnyProp, dontMatchWholeObject) {
|
||||
var actualType = getTypeForFilter(actual);
|
||||
var expectedType = getTypeForFilter(expected);
|
||||
|
||||
if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
|
||||
return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
|
||||
return !deepCompare(actual, expected.substring(1), comparator, anyPropertyKey, matchAgainstAnyProp);
|
||||
} else if (isArray(actual)) {
|
||||
// In case `actual` is an array, consider it a match
|
||||
// if ANY of it's items matches `expected`
|
||||
return actual.some(function(item) {
|
||||
return deepCompare(item, expected, comparator, matchAgainstAnyProp);
|
||||
return deepCompare(item, expected, comparator, anyPropertyKey, matchAgainstAnyProp);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -220,11 +226,11 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatc
|
||||
var key;
|
||||
if (matchAgainstAnyProp) {
|
||||
for (key in actual) {
|
||||
if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
|
||||
if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, anyPropertyKey, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
|
||||
return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, anyPropertyKey, false);
|
||||
} else if (expectedType === 'object') {
|
||||
for (key in expected) {
|
||||
var expectedVal = expected[key];
|
||||
@@ -232,9 +238,9 @@ function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatc
|
||||
continue;
|
||||
}
|
||||
|
||||
var matchAnyProperty = key === '$';
|
||||
var matchAnyProperty = key === anyPropertyKey;
|
||||
var actualVal = matchAnyProperty ? actual : actual[key];
|
||||
if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
|
||||
if (!deepCompare(actualVal, expectedVal, comparator, anyPropertyKey, matchAnyProperty, matchAnyProperty)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,21 +6,22 @@
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Creates a new array or string containing only a specified number of elements. The elements
|
||||
* are taken from either the beginning or the end of the source array, string or number, as specified by
|
||||
* the value and sign (positive or negative) of `limit`. If a number is used as input, it is
|
||||
* converted to a string.
|
||||
* Creates a new array or string containing only a specified number of elements. The elements are
|
||||
* taken from either the beginning or the end of the source array, string or number, as specified by
|
||||
* the value and sign (positive or negative) of `limit`. Other array-like objects are also supported
|
||||
* (e.g. array subclasses, NodeLists, jqLite/jQuery collections etc). If a number is used as input,
|
||||
* it is converted to a string.
|
||||
*
|
||||
* @param {Array|string|number} input Source array, string or number to be limited.
|
||||
* @param {string|number} limit The length of the returned array or string. If the `limit` number
|
||||
* @param {Array|ArrayLike|string|number} input - Array/array-like, string or number to be limited.
|
||||
* @param {string|number} limit - The length of the returned array or string. If the `limit` number
|
||||
* is positive, `limit` number of items from the beginning of the source array/string are copied.
|
||||
* If the number is negative, `limit` number of items from the end of the source array/string
|
||||
* are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
|
||||
* the input will be returned unchanged.
|
||||
* @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
|
||||
* indicates an offset from the end of `input`. Defaults to `0`.
|
||||
* @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
|
||||
* had less than `limit` elements.
|
||||
* @param {(string|number)=} begin - Index at which to begin limitation. As a negative index,
|
||||
* `begin` indicates an offset from the end of `input`. Defaults to `0`.
|
||||
* @returns {Array|string} A new sub-array or substring of length `limit` or less if the input had
|
||||
* less than `limit` elements.
|
||||
*
|
||||
* @example
|
||||
<example module="limitToExample">
|
||||
@@ -108,19 +109,25 @@ function limitToFilter() {
|
||||
if (isNaN(limit)) return input;
|
||||
|
||||
if (isNumber(input)) input = input.toString();
|
||||
if (!isArray(input) && !isString(input)) return input;
|
||||
if (!isArrayLike(input)) return input;
|
||||
|
||||
begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
|
||||
begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
|
||||
|
||||
if (limit >= 0) {
|
||||
return input.slice(begin, begin + limit);
|
||||
return sliceFn(input, begin, begin + limit);
|
||||
} else {
|
||||
if (begin === 0) {
|
||||
return input.slice(limit, input.length);
|
||||
return sliceFn(input, limit, input.length);
|
||||
} else {
|
||||
return input.slice(Math.max(0, begin + limit), begin);
|
||||
return sliceFn(input, Math.max(0, begin + limit), begin);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function sliceFn(input, begin, end) {
|
||||
if (isString(input)) return input.slice(begin, end);
|
||||
|
||||
return slice.call(input, begin, end);
|
||||
}
|
||||
|
||||
@@ -6,44 +6,128 @@
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
|
||||
* for strings and numerically for numbers. Note: if you notice numbers are not being sorted
|
||||
* as expected, make sure they are actually being saved as numbers and not strings.
|
||||
* Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) are also supported.
|
||||
* Returns an array containing the items from the specified `collection`, ordered by a `comparator`
|
||||
* function based on the values computed using the `expression` predicate.
|
||||
*
|
||||
* @param {Array} array The array (or array-like object) to sort.
|
||||
* @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
|
||||
* used by the comparator to determine the order of elements.
|
||||
* For example, `[{id: 'foo'}, {id: 'bar'}] | orderBy:'id'` would result in
|
||||
* `[{id: 'bar'}, {id: 'foo'}]`.
|
||||
*
|
||||
* The `collection` can be an Array or array-like object (e.g. NodeList, jQuery object, TypedArray,
|
||||
* String, etc).
|
||||
*
|
||||
* The `expression` can be a single predicate, or a list of predicates each serving as a tie-breaker
|
||||
* for the preceeding one. The `expression` is evaluated against each item and the output is used
|
||||
* for comparing with other items.
|
||||
*
|
||||
* You can change the sorting order by setting `reverse` to `true`. By default, items are sorted in
|
||||
* ascending order.
|
||||
*
|
||||
* The comparison is done using the `comparator` function. If none is specified, a default, built-in
|
||||
* comparator is used (see below for details - in a nutshell, it compares numbers numerically and
|
||||
* strings alphabetically).
|
||||
*
|
||||
* ### Under the hood
|
||||
*
|
||||
* Ordering the specified `collection` happens in two phases:
|
||||
*
|
||||
* 1. All items are passed through the predicate (or predicates), and the returned values are saved
|
||||
* along with their type (`string`, `number` etc). For example, an item `{label: 'foo'}`, passed
|
||||
* through a predicate that extracts the value of the `label` property, would be transformed to:
|
||||
* ```
|
||||
* {
|
||||
* value: 'foo',
|
||||
* type: 'string',
|
||||
* index: ...
|
||||
* }
|
||||
* ```
|
||||
* 2. The comparator function is used to sort the items, based on the derived values, types and
|
||||
* indices.
|
||||
*
|
||||
* If you use a custom comparator, it will be called with pairs of objects of the form
|
||||
* `{value: ..., type: '...', index: ...}` and is expected to return `0` if the objects are equal
|
||||
* (as far as the comparator is concerned), `-1` if the 1st one should be ranked higher than the
|
||||
* second, or `1` otherwise.
|
||||
*
|
||||
* In order to ensure that the sorting will be deterministic across platforms, if none of the
|
||||
* specified predicates can distinguish between two items, `orderBy` will automatically introduce a
|
||||
* dummy predicate that returns the item's index as `value`.
|
||||
* (If you are using a custom comparator, make sure it can handle this predicate as well.)
|
||||
*
|
||||
* Finally, in an attempt to simplify things, if a predicate returns an object as the extracted
|
||||
* value for an item, `orderBy` will try to convert that object to a primitive value, before passing
|
||||
* it to the comparator. The following rules govern the conversion:
|
||||
*
|
||||
* 1. If the object has a `valueOf()` method that returns a primitive, its return value will be
|
||||
* used instead.<br />
|
||||
* (If the object has a `valueOf()` method that returns another object, then the returned object
|
||||
* will be used in subsequent steps.)
|
||||
* 2. If the object has a custom `toString()` method (i.e. not the one inherited from `Object`) that
|
||||
* returns a primitive, its return value will be used instead.<br />
|
||||
* (If the object has a `toString()` method that returns another object, then the returned object
|
||||
* will be used in subsequent steps.)
|
||||
* 3. No conversion; the object itself is used.
|
||||
*
|
||||
* ### The default comparator
|
||||
*
|
||||
* The default, built-in comparator should be sufficient for most usecases. In short, it compares
|
||||
* numbers numerically, strings alphabetically (and case-insensitively), for objects falls back to
|
||||
* using their index in the original collection, and sorts values of different types by type.
|
||||
*
|
||||
* More specifically, it follows these steps to determine the relative order of items:
|
||||
*
|
||||
* 1. If the compared values are of different types, compare the types themselves alphabetically.
|
||||
* 2. If both values are of type `string`, compare them alphabetically in a case- and
|
||||
* locale-insensitive way.
|
||||
* 3. If both values are objects, compare their indices instead.
|
||||
* 4. Otherwise, return:
|
||||
* - `0`, if the values are equal (by strict equality comparison, i.e. using `===`).
|
||||
* - `-1`, if the 1st value is "less than" the 2nd value (compared using the `<` operator).
|
||||
* - `1`, otherwise.
|
||||
*
|
||||
* **Note:** If you notice numbers not being sorted as expected, make sure they are actually being
|
||||
* saved as numbers and not strings.
|
||||
*
|
||||
* @param {Array|ArrayLike} collection - The collection (array or array-like object) to sort.
|
||||
* @param {(Function|string|Array.<Function|string>)=} expression - A predicate (or list of
|
||||
* predicates) to be used by the comparator to determine the order of elements.
|
||||
*
|
||||
* Can be one of:
|
||||
*
|
||||
* - `function`: Getter function. The result of this function will be sorted using the
|
||||
* `<`, `===`, `>` operator.
|
||||
* - `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`). If no property is provided, (e.g. `'+'`) then the array
|
||||
* element itself is used to compare where sorting.
|
||||
* - `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.
|
||||
* - `Function`: A getter function. This function will be called with each item as argument and
|
||||
* the return value will be used for sorting.
|
||||
* - `string`: An Angular expression. This expression will be evaluated against each item and the
|
||||
* result will be used for sorting. For example, use `'label'` to sort by a property called
|
||||
* `label` or `'label.substring(0, 3)'` to sort by the first 3 characters of the `label`
|
||||
* property.<br />
|
||||
* (The result of a constant expression is interpreted as a property name to be used for
|
||||
* comparison. For example, use `'"special name"'` (note the extra pair of quotes) to sort by a
|
||||
* property called `special name`.)<br />
|
||||
* An expression can be optionally prefixed with `+` or `-` to control the sorting direction,
|
||||
* ascending or descending. For example, `'+label'` or `'-label'`. If no property is provided,
|
||||
* (e.g. `'+'` or `'-'`), the collection element itself is used in comparisons.
|
||||
* - `Array`: An array of function and/or string predicates. If a predicate cannot determine the
|
||||
* relative order of two items, the next predicate is used as a tie-breaker.
|
||||
*
|
||||
* If the predicate is missing or empty then it defaults to `'+'`.
|
||||
* **Note:** If the predicate is missing or empty then it defaults to `'+'`.
|
||||
*
|
||||
* @param {boolean=} reverse Reverse the order of the array.
|
||||
* @returns {Array} Sorted copy of the source array.
|
||||
* @param {boolean=} reverse - If `true`, reverse the sorting order.
|
||||
* @param {(Function)=} comparator - The comparator function used to determine the relative order of
|
||||
* value pairs. If omitted, the built-in comparator will be used.
|
||||
*
|
||||
* @returns {Array} - The sorted array.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* The example below demonstrates a simple ngRepeat, where the data is sorted
|
||||
* by age in descending order (predicate is set to `'-age'`).
|
||||
* `reverse` is not set, which means it defaults to `false`.
|
||||
<example module="orderByExample">
|
||||
* ### Ordering a table with `ngRepeat`
|
||||
*
|
||||
* The example below demonstrates a simple {@link ngRepeat ngRepeat}, where the data is sorted by
|
||||
* age in descending order (expression is set to `'-age'`). The `comparator` is not set, which means
|
||||
* it defaults to the built-in comparator.
|
||||
*
|
||||
<example name="orderBy-static" module="orderByExample1">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
<table class="friend">
|
||||
<table class="friends">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Phone Number</th>
|
||||
@@ -58,43 +142,77 @@
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('orderByExample', [])
|
||||
angular.module('orderByExample1', [])
|
||||
.controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.friends =
|
||||
[{name:'John', phone:'555-1212', age:10},
|
||||
{name:'Mary', phone:'555-9876', age:19},
|
||||
{name:'Mike', phone:'555-4321', age:21},
|
||||
{name:'Adam', phone:'555-5678', age:35},
|
||||
{name:'Julie', phone:'555-8765', age:29}];
|
||||
$scope.friends = [
|
||||
{name: 'John', phone: '555-1212', age: 10},
|
||||
{name: 'Mary', phone: '555-9876', age: 19},
|
||||
{name: 'Mike', phone: '555-4321', age: 21},
|
||||
{name: 'Adam', phone: '555-5678', age: 35},
|
||||
{name: 'Julie', phone: '555-8765', age: 29}
|
||||
];
|
||||
}]);
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.friends {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.friends th {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
.friends td, .friends th {
|
||||
border-left: 1px solid;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.friends td:first-child, .friends th:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
// Element locators
|
||||
var names = element.all(by.repeater('friends').column('friend.name'));
|
||||
|
||||
it('should sort friends by age in reverse order', function() {
|
||||
expect(names.get(0).getText()).toBe('Adam');
|
||||
expect(names.get(1).getText()).toBe('Julie');
|
||||
expect(names.get(2).getText()).toBe('Mike');
|
||||
expect(names.get(3).getText()).toBe('Mary');
|
||||
expect(names.get(4).getText()).toBe('John');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
* <hr />
|
||||
*
|
||||
* The predicate and reverse parameters can be controlled dynamically through scope properties,
|
||||
* as shown in the next example.
|
||||
* @example
|
||||
<example module="orderByExample">
|
||||
* ### Changing parameters dynamically
|
||||
*
|
||||
* All parameters can be changed dynamically. The next example shows how you can make the columns of
|
||||
* a table sortable, by binding the `expression` and `reverse` parameters to scope properties.
|
||||
*
|
||||
<example name="orderBy-dynamic" module="orderByExample2">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
<pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
|
||||
<pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
|
||||
<hr/>
|
||||
<button ng-click="predicate=''">Set to unsorted</button>
|
||||
<table class="friend">
|
||||
<button ng-click="propertyName = null; reverse = false">Set to unsorted</button>
|
||||
<hr/>
|
||||
<table class="friends">
|
||||
<tr>
|
||||
<th>
|
||||
<button ng-click="order('name')">Name</button>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="order('phone')">Phone Number</button>
|
||||
<span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="order('age')">Age</button>
|
||||
<span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="sortBy('name')">Name</button>
|
||||
<span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="sortBy('phone')">Phone Number</button>
|
||||
<span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="sortBy('age')">Age</button>
|
||||
<span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr ng-repeat="friend in friends | orderBy:predicate:reverse">
|
||||
<tr ng-repeat="friend in friends | orderBy:propertyName:reverse">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
<td>{{friend.age}}</td>
|
||||
@@ -103,100 +221,335 @@
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('orderByExample', [])
|
||||
angular.module('orderByExample2', [])
|
||||
.controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.friends =
|
||||
[{name:'John', phone:'555-1212', age:10},
|
||||
{name:'Mary', phone:'555-9876', age:19},
|
||||
{name:'Mike', phone:'555-4321', age:21},
|
||||
{name:'Adam', phone:'555-5678', age:35},
|
||||
{name:'Julie', phone:'555-8765', age:29}];
|
||||
$scope.predicate = 'age';
|
||||
var friends = [
|
||||
{name: 'John', phone: '555-1212', age: 10},
|
||||
{name: 'Mary', phone: '555-9876', age: 19},
|
||||
{name: 'Mike', phone: '555-4321', age: 21},
|
||||
{name: 'Adam', phone: '555-5678', age: 35},
|
||||
{name: 'Julie', phone: '555-8765', age: 29}
|
||||
];
|
||||
|
||||
$scope.propertyName = 'age';
|
||||
$scope.reverse = true;
|
||||
$scope.order = function(predicate) {
|
||||
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
|
||||
$scope.predicate = predicate;
|
||||
$scope.friends = friends;
|
||||
|
||||
$scope.sortBy = function(propertyName) {
|
||||
$scope.reverse = ($scope.propertyName === propertyName) ? !$scope.reverse : false;
|
||||
$scope.propertyName = propertyName;
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.friends {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.friends th {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
.friends td, .friends th {
|
||||
border-left: 1px solid;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.friends td:first-child, .friends th:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.sortorder:after {
|
||||
content: '\25b2';
|
||||
content: '\25b2'; // BLACK UP-POINTING TRIANGLE
|
||||
}
|
||||
.sortorder.reverse:after {
|
||||
content: '\25bc';
|
||||
content: '\25bc'; // BLACK DOWN-POINTING TRIANGLE
|
||||
}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
// Element locators
|
||||
var unsortButton = element(by.partialButtonText('unsorted'));
|
||||
var nameHeader = element(by.partialButtonText('Name'));
|
||||
var phoneHeader = element(by.partialButtonText('Phone'));
|
||||
var ageHeader = element(by.partialButtonText('Age'));
|
||||
var firstName = element(by.repeater('friends').column('friend.name').row(0));
|
||||
var lastName = element(by.repeater('friends').column('friend.name').row(4));
|
||||
|
||||
it('should sort friends by some property, when clicking on the column header', function() {
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
|
||||
phoneHeader.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Mary');
|
||||
|
||||
nameHeader.click();
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('Mike');
|
||||
|
||||
ageHeader.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Adam');
|
||||
});
|
||||
|
||||
it('should sort friends in reverse order, when clicking on the same column', function() {
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
|
||||
ageHeader.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Adam');
|
||||
|
||||
ageHeader.click();
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
});
|
||||
|
||||
it('should restore the original order, when clicking "Set to unsorted"', function() {
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
|
||||
unsortButton.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Julie');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
* <hr />
|
||||
*
|
||||
* @example
|
||||
* ### Using `orderBy` inside a controller
|
||||
*
|
||||
* It is also possible to call the `orderBy` filter manually, by injecting `orderByFilter`, and
|
||||
* calling it with the desired parameters. (Alternatively, you could inject the `$filter` factory
|
||||
* and retrieve the `orderBy` filter with `$filter('orderBy')`.)
|
||||
*
|
||||
<example name="orderBy-call-manually" module="orderByExample3">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
<pre>Sort by = {{propertyName}}; reverse = {{reverse}}</pre>
|
||||
<hr/>
|
||||
<button ng-click="sortBy(null)">Set to unsorted</button>
|
||||
<hr/>
|
||||
<table class="friends">
|
||||
<tr>
|
||||
<th>
|
||||
<button ng-click="sortBy('name')">Name</button>
|
||||
<span class="sortorder" ng-show="propertyName === 'name'" ng-class="{reverse: reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="sortBy('phone')">Phone Number</button>
|
||||
<span class="sortorder" ng-show="propertyName === 'phone'" ng-class="{reverse: reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="sortBy('age')">Age</button>
|
||||
<span class="sortorder" ng-show="propertyName === 'age'" ng-class="{reverse: reverse}"></span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr ng-repeat="friend in friends">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
<td>{{friend.age}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('orderByExample3', [])
|
||||
.controller('ExampleController', ['$scope', 'orderByFilter', function($scope, orderBy) {
|
||||
var friends = [
|
||||
{name: 'John', phone: '555-1212', age: 10},
|
||||
{name: 'Mary', phone: '555-9876', age: 19},
|
||||
{name: 'Mike', phone: '555-4321', age: 21},
|
||||
{name: 'Adam', phone: '555-5678', age: 35},
|
||||
{name: 'Julie', phone: '555-8765', age: 29}
|
||||
];
|
||||
|
||||
$scope.propertyName = 'age';
|
||||
$scope.reverse = true;
|
||||
$scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
|
||||
|
||||
$scope.sortBy = function(propertyName) {
|
||||
$scope.reverse = (propertyName !== null && $scope.propertyName === propertyName)
|
||||
? !$scope.reverse : false;
|
||||
$scope.propertyName = propertyName;
|
||||
$scope.friends = orderBy(friends, $scope.propertyName, $scope.reverse);
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.friends {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.friends th {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
.friends td, .friends th {
|
||||
border-left: 1px solid;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.friends td:first-child, .friends th:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
.sortorder:after {
|
||||
content: '\25b2'; // BLACK UP-POINTING TRIANGLE
|
||||
}
|
||||
.sortorder.reverse:after {
|
||||
content: '\25bc'; // BLACK DOWN-POINTING TRIANGLE
|
||||
}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
// Element locators
|
||||
var unsortButton = element(by.partialButtonText('unsorted'));
|
||||
var nameHeader = element(by.partialButtonText('Name'));
|
||||
var phoneHeader = element(by.partialButtonText('Phone'));
|
||||
var ageHeader = element(by.partialButtonText('Age'));
|
||||
var firstName = element(by.repeater('friends').column('friend.name').row(0));
|
||||
var lastName = element(by.repeater('friends').column('friend.name').row(4));
|
||||
|
||||
it('should sort friends by some property, when clicking on the column header', function() {
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
|
||||
phoneHeader.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Mary');
|
||||
|
||||
nameHeader.click();
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('Mike');
|
||||
|
||||
ageHeader.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Adam');
|
||||
});
|
||||
|
||||
it('should sort friends in reverse order, when clicking on the same column', function() {
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
|
||||
ageHeader.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Adam');
|
||||
|
||||
ageHeader.click();
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
});
|
||||
|
||||
it('should restore the original order, when clicking "Set to unsorted"', function() {
|
||||
expect(firstName.getText()).toBe('Adam');
|
||||
expect(lastName.getText()).toBe('John');
|
||||
|
||||
unsortButton.click();
|
||||
expect(firstName.getText()).toBe('John');
|
||||
expect(lastName.getText()).toBe('Julie');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
* <hr />
|
||||
*
|
||||
* @example
|
||||
* ### Using a custom comparator
|
||||
*
|
||||
* If you have very specific requirements about the way items are sorted, you can pass your own
|
||||
* comparator function. For example, you might need to compare some strings in a locale-sensitive
|
||||
* way. (When specifying a custom comparator, you also need to pass a value for the `reverse`
|
||||
* argument - passing `false` retains the default sorting order, i.e. ascending.)
|
||||
*
|
||||
<example name="orderBy-custom-comparator" module="orderByExample4">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
<div class="friends-container custom-comparator">
|
||||
<h3>Locale-sensitive Comparator</h3>
|
||||
<table class="friends">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Favorite Letter</th>
|
||||
</tr>
|
||||
<tr ng-repeat="friend in friends | orderBy:'favoriteLetter':false:localeSensitiveComparator">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.favoriteLetter}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="friends-container default-comparator">
|
||||
<h3>Default Comparator</h3>
|
||||
<table class="friends">
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Favorite Letter</th>
|
||||
</tr>
|
||||
<tr ng-repeat="friend in friends | orderBy:'favoriteLetter'">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.favoriteLetter}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('orderByExample4', [])
|
||||
.controller('ExampleController', ['$scope', function($scope) {
|
||||
$scope.friends = [
|
||||
{name: 'John', favoriteLetter: 'Ä'},
|
||||
{name: 'Mary', favoriteLetter: 'Ü'},
|
||||
{name: 'Mike', favoriteLetter: 'Ö'},
|
||||
{name: 'Adam', favoriteLetter: 'H'},
|
||||
{name: 'Julie', favoriteLetter: 'Z'}
|
||||
];
|
||||
|
||||
$scope.localeSensitiveComparator = function(v1, v2) {
|
||||
// If we don't get strings, just compare by index
|
||||
if (v1.type !== 'string' || v2.type !== 'string') {
|
||||
return (v1.index < v2.index) ? -1 : 1;
|
||||
}
|
||||
|
||||
// Compare strings alphabetically, taking locale into account
|
||||
return v1.value.localeCompare(v2.value);
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
<file name="style.css">
|
||||
.friends-container {
|
||||
display: inline-block;
|
||||
margin: 0 30px;
|
||||
}
|
||||
|
||||
.friends {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.friends th {
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
.friends td, .friends th {
|
||||
border-left: 1px solid;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.friends td:first-child, .friends th:first-child {
|
||||
border-left: none;
|
||||
}
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
// Element locators
|
||||
var container = element(by.css('.custom-comparator'));
|
||||
var names = container.all(by.repeater('friends').column('friend.name'));
|
||||
|
||||
it('should sort friends by favorite letter (in correct alphabetical order)', function() {
|
||||
expect(names.get(0).getText()).toBe('John');
|
||||
expect(names.get(1).getText()).toBe('Adam');
|
||||
expect(names.get(2).getText()).toBe('Mike');
|
||||
expect(names.get(3).getText()).toBe('Mary');
|
||||
expect(names.get(4).getText()).toBe('Julie');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*
|
||||
* It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
|
||||
* filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
|
||||
* desired parameters.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* @example
|
||||
<example module="orderByExample">
|
||||
<file name="index.html">
|
||||
<div ng-controller="ExampleController">
|
||||
<pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
|
||||
<table class="friend">
|
||||
<tr>
|
||||
<th>
|
||||
<button ng-click="order('name')">Name</button>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="order('phone')">Phone Number</button>
|
||||
<span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<button ng-click="order('age')">Age</button>
|
||||
<span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr ng-repeat="friend in friends">
|
||||
<td>{{friend.name}}</td>
|
||||
<td>{{friend.phone}}</td>
|
||||
<td>{{friend.age}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
angular.module('orderByExample', [])
|
||||
.controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
|
||||
var orderBy = $filter('orderBy');
|
||||
$scope.friends = [
|
||||
{ name: 'John', phone: '555-1212', age: 10 },
|
||||
{ name: 'Mary', phone: '555-9876', age: 19 },
|
||||
{ name: 'Mike', phone: '555-4321', age: 21 },
|
||||
{ name: 'Adam', phone: '555-5678', age: 35 },
|
||||
{ name: 'Julie', phone: '555-8765', age: 29 }
|
||||
];
|
||||
$scope.order = function(predicate) {
|
||||
$scope.predicate = predicate;
|
||||
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
|
||||
$scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
|
||||
};
|
||||
$scope.order('age', true);
|
||||
}]);
|
||||
</file>
|
||||
|
||||
<file name="style.css">
|
||||
.sortorder:after {
|
||||
content: '\25b2';
|
||||
}
|
||||
.sortorder.reverse:after {
|
||||
content: '\25bc';
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
orderByFilter.$inject = ['$parse'];
|
||||
function orderByFilter($parse) {
|
||||
return function(array, sortPredicate, reverseOrder) {
|
||||
return function(array, sortPredicate, reverseOrder, compareFn) {
|
||||
|
||||
if (array == null) return array;
|
||||
if (!isArrayLike(array)) {
|
||||
@@ -206,11 +559,12 @@ function orderByFilter($parse) {
|
||||
if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
|
||||
if (sortPredicate.length === 0) { sortPredicate = ['+']; }
|
||||
|
||||
var predicates = processPredicates(sortPredicate, reverseOrder);
|
||||
// Add a predicate at the end that evaluates to the element index. This makes the
|
||||
// sort stable as it works as a tie-breaker when all the input predicates cannot
|
||||
// distinguish between two elements.
|
||||
predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
|
||||
var predicates = processPredicates(sortPredicate);
|
||||
|
||||
var descending = reverseOrder ? -1 : 1;
|
||||
|
||||
// Define the `compare()` function. Use a default comparator if none is specified.
|
||||
var compare = isFunction(compareFn) ? compareFn : defaultCompare;
|
||||
|
||||
// The next three lines are a version of a Swartzian Transform idiom from Perl
|
||||
// (sometimes called the Decorate-Sort-Undecorate idiom)
|
||||
@@ -222,8 +576,12 @@ function orderByFilter($parse) {
|
||||
return array;
|
||||
|
||||
function getComparisonObject(value, index) {
|
||||
// NOTE: We are adding an extra `tieBreaker` value based on the element's index.
|
||||
// This will be used to keep the sort stable when none of the input predicates can
|
||||
// distinguish between two elements.
|
||||
return {
|
||||
value: value,
|
||||
tieBreaker: {value: index, type: 'number', index: index},
|
||||
predicateValues: predicates.map(function(predicate) {
|
||||
return getPredicateValue(predicate.get(value), index);
|
||||
})
|
||||
@@ -231,18 +589,19 @@ function orderByFilter($parse) {
|
||||
}
|
||||
|
||||
function doComparison(v1, v2) {
|
||||
var result = 0;
|
||||
for (var index=0, length = predicates.length; index < length; ++index) {
|
||||
result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
|
||||
if (result) break;
|
||||
for (var i = 0, ii = predicates.length; i < ii; i++) {
|
||||
var result = compare(v1.predicateValues[i], v2.predicateValues[i]);
|
||||
if (result) {
|
||||
return result * predicates[i].descending * descending;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
return compare(v1.tieBreaker, v2.tieBreaker) * descending;
|
||||
}
|
||||
};
|
||||
|
||||
function processPredicates(sortPredicate, reverseOrder) {
|
||||
reverseOrder = reverseOrder ? -1 : 1;
|
||||
return sortPredicate.map(function(predicate) {
|
||||
function processPredicates(sortPredicates) {
|
||||
return sortPredicates.map(function(predicate) {
|
||||
var descending = 1, get = identity;
|
||||
|
||||
if (isFunction(predicate)) {
|
||||
@@ -260,7 +619,7 @@ function orderByFilter($parse) {
|
||||
}
|
||||
}
|
||||
}
|
||||
return { get: get, descending: descending * reverseOrder };
|
||||
return {get: get, descending: descending};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -275,9 +634,9 @@ function orderByFilter($parse) {
|
||||
}
|
||||
}
|
||||
|
||||
function objectValue(value, index) {
|
||||
function objectValue(value) {
|
||||
// If `valueOf` is a valid function use that
|
||||
if (typeof value.valueOf === 'function') {
|
||||
if (isFunction(value.valueOf)) {
|
||||
value = value.valueOf();
|
||||
if (isPrimitive(value)) return value;
|
||||
}
|
||||
@@ -286,8 +645,8 @@ function orderByFilter($parse) {
|
||||
value = value.toString();
|
||||
if (isPrimitive(value)) return value;
|
||||
}
|
||||
// We have a basic object so we use the position of the object in the collection
|
||||
return index;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function getPredicateValue(value, index) {
|
||||
@@ -295,23 +654,39 @@ function orderByFilter($parse) {
|
||||
if (value === null) {
|
||||
type = 'string';
|
||||
value = 'null';
|
||||
} else if (type === 'string') {
|
||||
value = value.toLowerCase();
|
||||
} else if (type === 'object') {
|
||||
value = objectValue(value, index);
|
||||
value = objectValue(value);
|
||||
}
|
||||
return { value: value, type: type };
|
||||
return {value: value, type: type, index: index};
|
||||
}
|
||||
|
||||
function compare(v1, v2) {
|
||||
function defaultCompare(v1, v2) {
|
||||
var result = 0;
|
||||
if (v1.type === v2.type) {
|
||||
if (v1.value !== v2.value) {
|
||||
result = v1.value < v2.value ? -1 : 1;
|
||||
var type1 = v1.type;
|
||||
var type2 = v2.type;
|
||||
|
||||
if (type1 === type2) {
|
||||
var value1 = v1.value;
|
||||
var value2 = v2.value;
|
||||
|
||||
if (type1 === 'string') {
|
||||
// Compare strings case-insensitively
|
||||
value1 = value1.toLowerCase();
|
||||
value2 = value2.toLowerCase();
|
||||
} else if (type1 === 'object') {
|
||||
// For basic objects, use the position of the object
|
||||
// in the collection instead of the value
|
||||
if (isObject(value1)) value1 = v1.index;
|
||||
if (isObject(value2)) value2 = v2.index;
|
||||
}
|
||||
|
||||
if (value1 !== value2) {
|
||||
result = value1 < value2 ? -1 : 1;
|
||||
}
|
||||
} else {
|
||||
result = v1.type < v2.type ? -1 : 1;
|
||||
result = type1 < type2 ? -1 : 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ function $HttpParamSerializerProvider() {
|
||||
* * `{'foo': 'bar'}` results in `foo=bar`
|
||||
* * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
|
||||
* * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
|
||||
* * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
|
||||
* * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D` (stringified and encoded representation of an object)
|
||||
*
|
||||
* Note that serializer will sort the request parameters alphabetically.
|
||||
* */
|
||||
@@ -586,7 +586,7 @@ function $HttpProvider() {
|
||||
*
|
||||
* ### Overriding the Default Transformations Per Request
|
||||
*
|
||||
* If you wish override the request/response transformations only for a single request then provide
|
||||
* If you wish to override the request/response transformations only for a single request then provide
|
||||
* `transformRequest` and/or `transformResponse` properties on the configuration object passed
|
||||
* into `$http`.
|
||||
*
|
||||
@@ -629,7 +629,7 @@ function $HttpProvider() {
|
||||
* * cache a specific response - set config.cache value to TRUE or to a cache object
|
||||
*
|
||||
* If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
|
||||
* then the default `$cacheFactory($http)` object is used.
|
||||
* then the default `$cacheFactory("$http")` object is used.
|
||||
*
|
||||
* The default cache value can be set by updating the
|
||||
* {@link ng.$http#defaults `$http.defaults.cache`} property or the
|
||||
@@ -957,48 +957,25 @@ function $HttpProvider() {
|
||||
config.headers = mergeHeaders(requestConfig);
|
||||
config.method = uppercase(config.method);
|
||||
config.paramSerializer = isString(config.paramSerializer) ?
|
||||
$injector.get(config.paramSerializer) : config.paramSerializer;
|
||||
$injector.get(config.paramSerializer) : config.paramSerializer;
|
||||
|
||||
var serverRequest = function(config) {
|
||||
var headers = config.headers;
|
||||
var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
|
||||
|
||||
// strip content-type if data is undefined
|
||||
if (isUndefined(reqData)) {
|
||||
forEach(headers, function(value, header) {
|
||||
if (lowercase(header) === 'content-type') {
|
||||
delete headers[header];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
|
||||
config.withCredentials = defaults.withCredentials;
|
||||
}
|
||||
|
||||
// send request
|
||||
return sendReq(config, reqData).then(transformResponse, transformResponse);
|
||||
};
|
||||
|
||||
var chain = [serverRequest, undefined];
|
||||
var requestInterceptors = [];
|
||||
var responseInterceptors = [];
|
||||
var promise = $q.when(config);
|
||||
|
||||
// apply interceptors
|
||||
forEach(reversedInterceptors, function(interceptor) {
|
||||
if (interceptor.request || interceptor.requestError) {
|
||||
chain.unshift(interceptor.request, interceptor.requestError);
|
||||
requestInterceptors.unshift(interceptor.request, interceptor.requestError);
|
||||
}
|
||||
if (interceptor.response || interceptor.responseError) {
|
||||
chain.push(interceptor.response, interceptor.responseError);
|
||||
responseInterceptors.push(interceptor.response, interceptor.responseError);
|
||||
}
|
||||
});
|
||||
|
||||
while (chain.length) {
|
||||
var thenFn = chain.shift();
|
||||
var rejectFn = chain.shift();
|
||||
|
||||
promise = promise.then(thenFn, rejectFn);
|
||||
}
|
||||
promise = chainInterceptors(promise, requestInterceptors);
|
||||
promise = promise.then(serverRequest);
|
||||
promise = chainInterceptors(promise, responseInterceptors);
|
||||
|
||||
if (useLegacyPromise) {
|
||||
promise.success = function(fn) {
|
||||
@@ -1025,14 +1002,18 @@ function $HttpProvider() {
|
||||
|
||||
return promise;
|
||||
|
||||
function transformResponse(response) {
|
||||
// make a copy since the response must be cacheable
|
||||
var resp = extend({}, response);
|
||||
resp.data = transformData(response.data, response.headers, response.status,
|
||||
config.transformResponse);
|
||||
return (isSuccess(response.status))
|
||||
? resp
|
||||
: $q.reject(resp);
|
||||
|
||||
function chainInterceptors(promise, interceptors) {
|
||||
for (var i = 0, ii = interceptors.length; i < ii;) {
|
||||
var thenFn = interceptors[i++];
|
||||
var rejectFn = interceptors[i++];
|
||||
|
||||
promise = promise.then(thenFn, rejectFn);
|
||||
}
|
||||
|
||||
interceptors.length = 0;
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
function executeHeaderFns(headers, config) {
|
||||
@@ -1076,6 +1057,37 @@ function $HttpProvider() {
|
||||
// execute if header value is a function for merged headers
|
||||
return executeHeaderFns(reqHeaders, shallowCopy(config));
|
||||
}
|
||||
|
||||
function serverRequest(config) {
|
||||
var headers = config.headers;
|
||||
var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
|
||||
|
||||
// strip content-type if data is undefined
|
||||
if (isUndefined(reqData)) {
|
||||
forEach(headers, function(value, header) {
|
||||
if (lowercase(header) === 'content-type') {
|
||||
delete headers[header];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
|
||||
config.withCredentials = defaults.withCredentials;
|
||||
}
|
||||
|
||||
// send request
|
||||
return sendReq(config, reqData).then(transformResponse, transformResponse);
|
||||
}
|
||||
|
||||
function transformResponse(response) {
|
||||
// make a copy since the response must be cacheable
|
||||
var resp = extend({}, response);
|
||||
resp.data = transformData(response.data, response.headers, response.status,
|
||||
config.transformResponse);
|
||||
return (isSuccess(response.status))
|
||||
? resp
|
||||
: $q.reject(resp);
|
||||
}
|
||||
}
|
||||
|
||||
$http.pendingRequests = [];
|
||||
@@ -1122,6 +1134,8 @@ function $HttpProvider() {
|
||||
*
|
||||
* @description
|
||||
* Shortcut method to perform `JSONP` request.
|
||||
* If you would like to customise where and how the callbacks are stored then try overriding
|
||||
* or decorating the {@link $jsonpCallbacks} service.
|
||||
*
|
||||
* @param {string} url Relative or absolute URL specifying the destination of the request.
|
||||
* The name of the callback should be the string `JSON_CALLBACK`.
|
||||
|
||||
@@ -32,7 +32,7 @@ function $xhrFactoryProvider() {
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $httpBackend
|
||||
* @requires $window
|
||||
* @requires $jsonpCallbacks
|
||||
* @requires $document
|
||||
* @requires $xhrFactory
|
||||
*
|
||||
@@ -47,8 +47,8 @@ function $xhrFactoryProvider() {
|
||||
* $httpBackend} which can be trained with responses.
|
||||
*/
|
||||
function $HttpBackendProvider() {
|
||||
this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
|
||||
return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
|
||||
this.$get = ['$browser', '$jsonpCallbacks', '$document', '$xhrFactory', function($browser, $jsonpCallbacks, $document, $xhrFactory) {
|
||||
return createHttpBackend($browser, $xhrFactory, $browser.defer, $jsonpCallbacks, $document[0]);
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -58,17 +58,13 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
$browser.$$incOutstandingRequestCount();
|
||||
url = url || $browser.url();
|
||||
|
||||
if (lowercase(method) == 'jsonp') {
|
||||
var callbackId = '_' + (callbacks.counter++).toString(36);
|
||||
callbacks[callbackId] = function(data) {
|
||||
callbacks[callbackId].data = data;
|
||||
callbacks[callbackId].called = true;
|
||||
};
|
||||
|
||||
var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
|
||||
callbackId, function(status, text) {
|
||||
completeRequest(callback, status, callbacks[callbackId].data, "", text);
|
||||
callbacks[callbackId] = noop;
|
||||
if (lowercase(method) === 'jsonp') {
|
||||
var callbackPath = callbacks.createCallback(url);
|
||||
var jsonpDone = jsonpReq(url, callbackPath, function(status, text) {
|
||||
// jsonpReq only ever sets status to 200 (OK), 404 (ERROR) or -1 (WAITING)
|
||||
var response = (status === 200) && callbacks.getResponse(callbackPath);
|
||||
completeRequest(callback, status, response, "", text);
|
||||
callbacks.removeCallback(callbackPath);
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -170,7 +166,8 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
}
|
||||
};
|
||||
|
||||
function jsonpReq(url, callbackId, done) {
|
||||
function jsonpReq(url, callbackPath, done) {
|
||||
url = url.replace('JSON_CALLBACK', callbackPath);
|
||||
// we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
|
||||
// - fetches local scripts via XHR and evals them
|
||||
// - adds and immediately removes script elements from the document
|
||||
@@ -188,7 +185,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
var text = "unknown";
|
||||
|
||||
if (event) {
|
||||
if (event.type === "load" && !callbacks[callbackId].called) {
|
||||
if (event.type === "load" && !callbacks.wasCalled(callbackPath)) {
|
||||
event = { type: "error" };
|
||||
}
|
||||
text = event.type;
|
||||
|
||||
@@ -180,7 +180,7 @@ function $InterpolateProvider() {
|
||||
*
|
||||
* `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
|
||||
*
|
||||
* ####Escaped Interpolation
|
||||
* #### Escaped Interpolation
|
||||
* $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
|
||||
* can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
|
||||
* It will be rendered as a regular start/end marker, and will not be interpreted as an expression
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $jsonpCallbacks
|
||||
* @requires $window
|
||||
* @description
|
||||
* This service handles the lifecycle of callbacks to handle JSONP requests.
|
||||
* Override this service if you wish to customise where the callbacks are stored and
|
||||
* how they vary compared to the requested url.
|
||||
*/
|
||||
var $jsonpCallbacksProvider = function() {
|
||||
this.$get = ['$window', function($window) {
|
||||
var callbacks = $window.angular.callbacks;
|
||||
var callbackMap = {};
|
||||
|
||||
function createCallback(callbackId) {
|
||||
var callback = function(data) {
|
||||
callback.data = data;
|
||||
callback.called = true;
|
||||
};
|
||||
callback.id = callbackId;
|
||||
return callback;
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $jsonpCallbacks#createCallback
|
||||
* @param {string} url the url of the JSONP request
|
||||
* @returns {string} the callback path to send to the server as part of the JSONP request
|
||||
* @description
|
||||
* {@link $httpBackend} calls this method to create a callback and get hold of the path to the callback
|
||||
* to pass to the server, which will be used to call the callback with its payload in the JSONP response.
|
||||
*/
|
||||
createCallback: function(url) {
|
||||
var callbackId = '_' + (callbacks.$$counter++).toString(36);
|
||||
var callbackPath = 'angular.callbacks.' + callbackId;
|
||||
var callback = createCallback(callbackId);
|
||||
callbackMap[callbackPath] = callbacks[callbackId] = callback;
|
||||
return callbackPath;
|
||||
},
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $jsonpCallbacks#wasCalled
|
||||
* @param {string} callbackPath the path to the callback that was sent in the JSONP request
|
||||
* @returns {boolean} whether the callback has been called, as a result of the JSONP response
|
||||
* @description
|
||||
* {@link $httpBackend} calls this method to find out whether the JSONP response actually called the
|
||||
* callback that was passed in the request.
|
||||
*/
|
||||
wasCalled: function(callbackPath) {
|
||||
return callbackMap[callbackPath].called;
|
||||
},
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $jsonpCallbacks#getResponse
|
||||
* @param {string} callbackPath the path to the callback that was sent in the JSONP request
|
||||
* @returns {*} the data received from the response via the registered callback
|
||||
* @description
|
||||
* {@link $httpBackend} calls this method to get hold of the data that was provided to the callback
|
||||
* in the JSONP response.
|
||||
*/
|
||||
getResponse: function(callbackPath) {
|
||||
return callbackMap[callbackPath].data;
|
||||
},
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $jsonpCallbacks#removeCallback
|
||||
* @param {string} callbackPath the path to the callback that was sent in the JSONP request
|
||||
* @description
|
||||
* {@link $httpBackend} calls this method to remove the callback after the JSONP request has
|
||||
* completed or timed-out.
|
||||
*/
|
||||
removeCallback: function(callbackPath) {
|
||||
var callback = callbackMap[callbackPath];
|
||||
delete callbacks[callback.id];
|
||||
delete callbackMap[callbackPath];
|
||||
}
|
||||
};
|
||||
}];
|
||||
};
|
||||
@@ -328,6 +328,12 @@ function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
|
||||
|
||||
var locationPrototype = {
|
||||
|
||||
/**
|
||||
* Ensure absolute url is initialized.
|
||||
* @private
|
||||
*/
|
||||
$$absUrl:'',
|
||||
|
||||
/**
|
||||
* Are we in html5 mode?
|
||||
* @private
|
||||
|
||||
@@ -545,7 +545,7 @@ AST.prototype = {
|
||||
var args = [];
|
||||
if (this.peekToken().text !== ')') {
|
||||
do {
|
||||
args.push(this.expression());
|
||||
args.push(this.filterChain());
|
||||
} while (this.expect(','));
|
||||
}
|
||||
return args;
|
||||
|
||||
@@ -135,7 +135,7 @@
|
||||
*
|
||||
* **Methods**
|
||||
*
|
||||
* - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
|
||||
* - `then(successCallback, [errorCallback], [notifyCallback])` – regardless of when the promise was or
|
||||
* will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
|
||||
* as soon as the result is available. The callbacks are called with a single argument: the result
|
||||
* or rejection reason. Additionally, the notify callback may be called zero or more times to
|
||||
@@ -146,7 +146,8 @@
|
||||
* with the value which is resolved in that promise using
|
||||
* [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
|
||||
* It also notifies via the return value of the `notifyCallback` method. The promise cannot be
|
||||
* resolved or rejected from the notifyCallback method.
|
||||
* resolved or rejected from the notifyCallback method. The errorCallback and notifyCallback
|
||||
* arguments are optional.
|
||||
*
|
||||
* - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
|
||||
*
|
||||
@@ -561,6 +562,30 @@ function qFactory(nextTick, exceptionHandler) {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $q#race
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Returns a promise that resolves or rejects as soon as one of those promises
|
||||
* resolves or rejects, with the value or reason from that promise.
|
||||
*
|
||||
* @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
|
||||
* @returns {Promise} a promise that resolves or rejects as soon as one of the `promises`
|
||||
* resolves or rejects, with the value or reason from that promise.
|
||||
*/
|
||||
|
||||
function race(promises) {
|
||||
var deferred = defer();
|
||||
|
||||
forEach(promises, function(promise) {
|
||||
when(promise).then(deferred.resolve, deferred.reject);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
var $Q = function Q(resolver) {
|
||||
if (!isFunction(resolver)) {
|
||||
throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
|
||||
@@ -590,6 +615,7 @@ function qFactory(nextTick, exceptionHandler) {
|
||||
$Q.when = when;
|
||||
$Q.resolve = resolve;
|
||||
$Q.all = all;
|
||||
$Q.race = race;
|
||||
|
||||
return $Q;
|
||||
}
|
||||
|
||||
@@ -6,69 +6,81 @@
|
||||
"window": false,
|
||||
|
||||
"angular": false,
|
||||
"noop": false,
|
||||
|
||||
"copy": false,
|
||||
"forEach": false,
|
||||
"extend": false,
|
||||
"jqLite": false,
|
||||
"forEach": false,
|
||||
"isArray": false,
|
||||
"isString": false,
|
||||
"isObject": false,
|
||||
"isUndefined": false,
|
||||
"isDefined": false,
|
||||
"isFunction": false,
|
||||
"isElement": false,
|
||||
/* ng helpers */
|
||||
"copy": true,
|
||||
"extend": true,
|
||||
"forEach": true,
|
||||
"isArray": true,
|
||||
"isDefined": true,
|
||||
"isElement": true,
|
||||
"isFunction": true,
|
||||
"isObject": true,
|
||||
"isString": true,
|
||||
"isUndefined": true,
|
||||
"jqLite": true,
|
||||
"noop": true,
|
||||
|
||||
"ELEMENT_NODE": false,
|
||||
"COMMENT_NODE": false,
|
||||
"NG_ANIMATE_CLASSNAME": false,
|
||||
"NG_ANIMATE_CHILDREN_DATA": false,
|
||||
/* ngAnimate constants */
|
||||
"COMMENT_NODE": true,
|
||||
"ELEMENT_NODE": true,
|
||||
"NG_ANIMATE_CLASSNAME": true,
|
||||
"NG_ANIMATE_CHILDREN_DATA": true,
|
||||
|
||||
"ADD_CLASS_SUFFIX": false,
|
||||
"REMOVE_CLASS_SUFFIX": false,
|
||||
"EVENT_CLASS_PREFIX": false,
|
||||
"ACTIVE_CLASS_SUFFIX": false,
|
||||
"PREPARE_CLASS_SUFFIX": false,
|
||||
/* ngAnimate className constants */
|
||||
"ADD_CLASS_SUFFIX": true,
|
||||
"REMOVE_CLASS_SUFFIX": true,
|
||||
"EVENT_CLASS_PREFIX": true,
|
||||
"ACTIVE_CLASS_SUFFIX": true,
|
||||
"PREPARE_CLASS_SUFFIX": true,
|
||||
|
||||
"TRANSITION_DURATION_PROP": false,
|
||||
"TRANSITION_DELAY_PROP": false,
|
||||
"TRANSITION_PROP": false,
|
||||
"PROPERTY_KEY": false,
|
||||
"DURATION_KEY": false,
|
||||
"DELAY_KEY": false,
|
||||
"TIMING_KEY": false,
|
||||
"ANIMATION_DURATION_PROP": false,
|
||||
"ANIMATION_DELAY_PROP": false,
|
||||
"ANIMATION_PROP": false,
|
||||
"ANIMATION_ITERATION_COUNT_KEY": false,
|
||||
"SAFE_FAST_FORWARD_DURATION_VALUE": false,
|
||||
"TRANSITIONEND_EVENT": false,
|
||||
"ANIMATIONEND_EVENT": false,
|
||||
/* ngAnimate CSS constants */
|
||||
"ANIMATION_DELAY_PROP": true,
|
||||
"ANIMATION_DURATION_PROP": true,
|
||||
"ANIMATION_ITERATION_COUNT_KEY": true,
|
||||
"ANIMATION_PROP": true,
|
||||
"ANIMATIONEND_EVENT": true,
|
||||
"DELAY_KEY": true,
|
||||
"DURATION_KEY": true,
|
||||
"PROPERTY_KEY": true,
|
||||
"SAFE_FAST_FORWARD_DURATION_VALUE": true,
|
||||
"TIMING_KEY": true,
|
||||
"TRANSITION_DELAY_PROP": true,
|
||||
"TRANSITION_DURATION_PROP": true,
|
||||
"TRANSITION_PROP": true,
|
||||
"TRANSITIONEND_EVENT": true,
|
||||
|
||||
"assertArg": false,
|
||||
"isPromiseLike": false,
|
||||
"mergeClasses": false,
|
||||
"mergeAnimationDetails": false,
|
||||
"prepareAnimationOptions": false,
|
||||
"applyAnimationStyles": false,
|
||||
"applyAnimationFromStyles": false,
|
||||
"applyAnimationToStyles": false,
|
||||
/* ngAnimate helpers */
|
||||
"applyAnimationClassesFactory": false,
|
||||
"pendClasses": false,
|
||||
"normalizeCssProp": false,
|
||||
"packageStyles": false,
|
||||
"removeFromArray": false,
|
||||
"stripCommentsFromElement": false,
|
||||
"applyAnimationFromStyles": false,
|
||||
"applyAnimationStyles": false,
|
||||
"applyAnimationToStyles": false,
|
||||
"applyGeneratedPreparationClasses": false,
|
||||
"applyInlineStyle": false,
|
||||
"assertArg": false,
|
||||
"blockKeyframeAnimations": false,
|
||||
"blockTransitions": false,
|
||||
"clearGeneratedClasses": false,
|
||||
"concatWithSpace": false,
|
||||
"extractElementNode": false,
|
||||
"getDomNode": false,
|
||||
"mergeAnimationDetails": false,
|
||||
"mergeClasses": false,
|
||||
"packageStyles": false,
|
||||
"pendClasses": false,
|
||||
"prepareAnimationOptions": false,
|
||||
"removeFromArray": false,
|
||||
"stripCommentsFromElement": false,
|
||||
|
||||
"applyGeneratedPreparationClasses": false,
|
||||
"clearGeneratedClasses": false,
|
||||
"blockTransitions": false,
|
||||
"blockKeyframeAnimations": false,
|
||||
"applyInlineStyle": false,
|
||||
"concatWithSpace": false
|
||||
/* ngAnimate directives/services */
|
||||
"ngAnimateSwapDirective": true,
|
||||
"$$rAFSchedulerFactory": true,
|
||||
"$$AnimateChildrenDirective": true,
|
||||
"$$AnimateQueueProvider": true,
|
||||
"$$AnimationProvider": true,
|
||||
"$AnimateCssProvider": true,
|
||||
"$$AnimateCssDriverProvider": true,
|
||||
"$$AnimateJsProvider": true,
|
||||
"$$AnimateJsDriverProvider": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ var $$AnimateChildrenDirective = ['$interpolate', function($interpolate) {
|
||||
return {
|
||||
link: function(scope, element, attrs) {
|
||||
var val = attrs.ngAnimateChildren;
|
||||
if (angular.isString(val) && val.length === 0) { //empty attribute
|
||||
if (isString(val) && val.length === 0) { //empty attribute
|
||||
element.data(NG_ANIMATE_CHILDREN_DATA, true);
|
||||
} else {
|
||||
// Interpolate and set the value, so that it is available to
|
||||
|
||||
@@ -156,7 +156,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
}
|
||||
);
|
||||
|
||||
var callbackRegistry = {};
|
||||
var callbackRegistry = Object.create(null);
|
||||
|
||||
// remember that the classNameFilter is set during the provider/config
|
||||
// stage therefore we can optimize here and setup a helper function
|
||||
@@ -239,7 +239,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
},
|
||||
|
||||
off: function(event, container, callback) {
|
||||
if (arguments.length === 1 && !angular.isString(arguments[0])) {
|
||||
if (arguments.length === 1 && !isString(arguments[0])) {
|
||||
container = arguments[0];
|
||||
for (var eventType in callbackRegistry) {
|
||||
callbackRegistry[eventType] = filterFromRegistry(callbackRegistry[eventType], container);
|
||||
@@ -287,11 +287,10 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
|
||||
bool = animationsEnabled = !!element;
|
||||
} else {
|
||||
var node = getDomNode(element);
|
||||
var recordExists = disabledElementsLookup.get(node);
|
||||
|
||||
if (argCount === 1) {
|
||||
// (element) - Element getter
|
||||
bool = !recordExists;
|
||||
bool = !disabledElementsLookup.get(node);
|
||||
} else {
|
||||
// (element, bool) - Element setter
|
||||
disabledElementsLookup.put(node, !bool);
|
||||
|
||||
@@ -348,8 +348,6 @@ var $$AnimationProvider = ['$animateProvider', function($animateProvider) {
|
||||
// may attempt more elements, but custom drivers are more particular
|
||||
for (var i = drivers.length - 1; i >= 0; i--) {
|
||||
var driverName = drivers[i];
|
||||
if (!$injector.has(driverName)) continue; // TODO(matsko): remove this check
|
||||
|
||||
var factory = $injector.get(driverName);
|
||||
var driver = factory(animationDetails);
|
||||
if (driver) {
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/* global angularAnimateModule: true,
|
||||
|
||||
ngAnimateSwapDirective,
|
||||
$$AnimateAsyncRunFactory,
|
||||
$$rAFSchedulerFactory,
|
||||
$$AnimateChildrenDirective,
|
||||
$$AnimateQueueProvider,
|
||||
$$AnimationProvider,
|
||||
$AnimateCssProvider,
|
||||
$$AnimateCssDriverProvider,
|
||||
$$AnimateJsProvider,
|
||||
$$AnimateJsDriverProvider,
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngAnimate
|
||||
@@ -730,6 +716,19 @@
|
||||
* (Note that you will need to trigger a digest within the callback to get angular to notice any scope-related changes.)
|
||||
*/
|
||||
|
||||
var copy;
|
||||
var extend;
|
||||
var forEach;
|
||||
var isArray;
|
||||
var isDefined;
|
||||
var isElement;
|
||||
var isFunction;
|
||||
var isObject;
|
||||
var isString;
|
||||
var isUndefined;
|
||||
var jqLite;
|
||||
var noop;
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $animate
|
||||
@@ -740,7 +739,22 @@
|
||||
*
|
||||
* Click here {@link ng.$animate to learn more about animations with `$animate`}.
|
||||
*/
|
||||
angular.module('ngAnimate', [])
|
||||
angular.module('ngAnimate', [], function initAngularHelpers() {
|
||||
// Access helpers from angular core.
|
||||
// Do it inside a `config` block to ensure `window.angular` is available.
|
||||
noop = angular.noop;
|
||||
copy = angular.copy;
|
||||
extend = angular.extend;
|
||||
jqLite = angular.element;
|
||||
forEach = angular.forEach;
|
||||
isArray = angular.isArray;
|
||||
isString = angular.isString;
|
||||
isObject = angular.isObject;
|
||||
isUndefined = angular.isUndefined;
|
||||
isDefined = angular.isDefined;
|
||||
isFunction = angular.isFunction;
|
||||
isElement = angular.isElement;
|
||||
})
|
||||
.directive('ngAnimateSwap', ngAnimateSwapDirective)
|
||||
|
||||
.directive('ngAnimateChildren', $$AnimateChildrenDirective)
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/* jshint ignore:start */
|
||||
var noop = angular.noop;
|
||||
var copy = angular.copy;
|
||||
var extend = angular.extend;
|
||||
var jqLite = angular.element;
|
||||
var forEach = angular.forEach;
|
||||
var isArray = angular.isArray;
|
||||
var isString = angular.isString;
|
||||
var isObject = angular.isObject;
|
||||
var isUndefined = angular.isUndefined;
|
||||
var isDefined = angular.isDefined;
|
||||
var isFunction = angular.isFunction;
|
||||
var isElement = angular.isElement;
|
||||
|
||||
var ELEMENT_NODE = 1;
|
||||
var COMMENT_NODE = 8;
|
||||
|
||||
@@ -38,7 +24,7 @@ var CSS_PREFIX = '', TRANSITION_PROP, TRANSITIONEND_EVENT, ANIMATION_PROP, ANIMA
|
||||
// Also, the only modern browser that uses vendor prefixes for transitions/keyframes is webkit
|
||||
// therefore there is no reason to test anymore for other vendor prefixes:
|
||||
// http://caniuse.com/#search=transition
|
||||
if (isUndefined(window.ontransitionend) && isDefined(window.onwebkittransitionend)) {
|
||||
if ((window.ontransitionend === void 0) && (window.onwebkittransitionend !== void 0)) {
|
||||
CSS_PREFIX = '-webkit-';
|
||||
TRANSITION_PROP = 'WebkitTransition';
|
||||
TRANSITIONEND_EVENT = 'webkitTransitionEnd transitionend';
|
||||
@@ -47,7 +33,7 @@ if (isUndefined(window.ontransitionend) && isDefined(window.onwebkittransitionen
|
||||
TRANSITIONEND_EVENT = 'transitionend';
|
||||
}
|
||||
|
||||
if (isUndefined(window.onanimationend) && isDefined(window.onwebkitanimationend)) {
|
||||
if ((window.onanimationend === void 0) && (window.onwebkitanimationend !== void 0)) {
|
||||
CSS_PREFIX = '-webkit-';
|
||||
ANIMATION_PROP = 'WebkitAnimation';
|
||||
ANIMATIONEND_EVENT = 'webkitAnimationEnd animationend';
|
||||
@@ -69,10 +55,6 @@ var ANIMATION_DURATION_PROP = ANIMATION_PROP + DURATION_KEY;
|
||||
var TRANSITION_DELAY_PROP = TRANSITION_PROP + DELAY_KEY;
|
||||
var TRANSITION_DURATION_PROP = TRANSITION_PROP + DURATION_KEY;
|
||||
|
||||
var isPromiseLike = function(p) {
|
||||
return p && p.then ? true : false;
|
||||
};
|
||||
|
||||
var ngMinErr = angular.$$minErr('ng');
|
||||
function assertArg(arg, name, reason) {
|
||||
if (!arg) {
|
||||
@@ -128,7 +110,6 @@ function stripCommentsFromElement(element) {
|
||||
switch (element.length) {
|
||||
case 0:
|
||||
return element;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// there is no point of stripping anything if the element
|
||||
@@ -141,7 +122,6 @@ function stripCommentsFromElement(element) {
|
||||
|
||||
default:
|
||||
return jqLite(extractElementNode(element));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +162,7 @@ function applyAnimationClassesFactory($$jqLite) {
|
||||
$$removeClass($$jqLite, element, options.removeClass);
|
||||
options.removeClass = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function prepareAnimationOptions(options) {
|
||||
@@ -285,10 +265,10 @@ function resolveElementClasses(existing, toAdd, toRemove) {
|
||||
var prop, allow;
|
||||
if (val === ADD_CLASS) {
|
||||
prop = 'addClass';
|
||||
allow = !existing[klass];
|
||||
allow = !existing[klass] || existing[klass + REMOVE_CLASS_SUFFIX];
|
||||
} else if (val === REMOVE_CLASS) {
|
||||
prop = 'removeClass';
|
||||
allow = existing[klass];
|
||||
allow = existing[klass] || existing[klass + ADD_CLASS_SUFFIX];
|
||||
}
|
||||
if (allow) {
|
||||
if (classes[prop].length) {
|
||||
@@ -318,7 +298,7 @@ function resolveElementClasses(existing, toAdd, toRemove) {
|
||||
}
|
||||
|
||||
function getDomNode(element) {
|
||||
return (element instanceof angular.element) ? element[0] : element;
|
||||
return (element instanceof jqLite) ? element[0] : element;
|
||||
}
|
||||
|
||||
function applyGeneratedPreparationClasses(element, event, options) {
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
* @name ngComponentRouter
|
||||
* @installation
|
||||
*
|
||||
* <div class="alert alert-info">
|
||||
* **Deprecation Notice:** In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module) has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
* </div>
|
||||
*
|
||||
* Currently, the **Component Router** module must be installed via `npm`, it is not yet available
|
||||
* on Bower or the Google CDN.
|
||||
*
|
||||
@@ -15,7 +20,8 @@
|
||||
* <script src="/node_modules/@angular/router/angular1/angular_1_router.js"></script>
|
||||
*```
|
||||
*
|
||||
* You also need to include ES6 shims to support running on Internet Explorer:
|
||||
* You also need to include ES6 shims for browsers that do not support ES6 code (Internet Explorer,
|
||||
iOs < 8, Android < 5.0, Windows Mobile < 10):
|
||||
* ```html
|
||||
* <!-- IE required polyfills, in this exact order -->
|
||||
* <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.min.js"></script>
|
||||
@@ -45,6 +51,11 @@
|
||||
* You can see the state of a router by inspecting the read-only field `router.navigating`.
|
||||
* This may be useful for showing a spinner, for instance.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -57,6 +68,12 @@
|
||||
* Apart from the **Top Level Component** ({@link $routerRootComponent}) which is associated with
|
||||
* the {@link $rootRouter}, every **Routing Component** is associated with a `ChildRouter`,
|
||||
* which manages the routing for that **Routing Component**.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -69,12 +86,19 @@
|
||||
* There is only one instance of this type in a Component Router application injectable as the
|
||||
* {@link $rootRouter} service. This **Router** is associate with the **Top Level Component**
|
||||
* ({@link $routerRootComponent}). It acts as the connection between the **Routers** and the **Location**.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc type
|
||||
* @name ComponentInstruction
|
||||
* @description
|
||||
*
|
||||
* A `ComponentInstruction` represents the route state for a single component. An `Instruction` is
|
||||
* composed of a tree of these `ComponentInstruction`s.
|
||||
*
|
||||
@@ -82,6 +106,12 @@
|
||||
* to route lifecycle hooks, like `$routerCanActivate`.
|
||||
*
|
||||
* You should not modify this object. It should be treated as immutable.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -96,14 +126,27 @@
|
||||
* * `component`, `loader`, `redirectTo` (requires exactly one of these)
|
||||
* * `name` - the name used to identify the **Route Definition** when generating links
|
||||
* * `data` (optional)
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc type
|
||||
* @name RouteParams
|
||||
* @description
|
||||
*
|
||||
* A map of parameters for a given route, passed as part of the {@link ComponentInstruction} to
|
||||
* the Lifecycle Hooks, such as `$routerOnActivate` and `$routerOnDeactivate`.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -114,6 +157,12 @@
|
||||
* @description
|
||||
*
|
||||
* The directive that identifies where the {@link Router} should render its **Components**.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -128,6 +177,12 @@
|
||||
* ```html
|
||||
* <a ng-link="['Parent', {param: 1}, 'Child']">Link to Child View</a>
|
||||
* ````
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
|
||||
@@ -135,8 +190,15 @@
|
||||
* @ngdoc service
|
||||
* @name $rootRouter
|
||||
* @description
|
||||
*
|
||||
* The singleton instance of the {@link RootRouter} type, which is associated
|
||||
* with the top level {@link $routerRootComponent}.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
|
||||
@@ -146,4 +208,10 @@
|
||||
* @description
|
||||
*
|
||||
* The top level **Routing Component** associated with the {@link $rootRouter}.
|
||||
*
|
||||
* @deprecated
|
||||
* In an effort to keep synchronized with router changes in Angular 2, this implementation of the Component Router (ngComponentRouter module)
|
||||
* has been deprecated and will not receive further updates.
|
||||
* We are investigating backporting the Angular 2 Router to Angular 1, but alternatively, use the {@link ngRoute} module or community developed
|
||||
* projects (e.g. [ui-router](https://github.com/angular-ui/ui-router)).
|
||||
*/
|
||||
|
||||
@@ -5,11 +5,9 @@
|
||||
// This file is compiled with Closure compiler's ADVANCED_OPTIMIZATIONS flag! Be wary of using
|
||||
// constructs incompatible with that mode.
|
||||
|
||||
var $interpolateMinErr = window['angular']['$interpolateMinErr'];
|
||||
|
||||
var noop = window['angular']['noop'],
|
||||
isFunction = window['angular']['isFunction'],
|
||||
toJson = window['angular']['toJson'];
|
||||
/* global isFunction: false */
|
||||
/* global noop: false */
|
||||
/* global toJson: false */
|
||||
|
||||
function stringify(value) {
|
||||
if (value == null /* null/undefined */) { return ''; }
|
||||
|
||||
@@ -5,31 +5,90 @@
|
||||
// This file is compiled with Closure compiler's ADVANCED_OPTIMIZATIONS flag! Be wary of using
|
||||
// constructs incompatible with that mode.
|
||||
|
||||
/* global $interpolateMinErr: false */
|
||||
/* global $interpolateMinErr: true */
|
||||
/* global isFunction: true */
|
||||
/* global noop: true */
|
||||
/* global toJson: true */
|
||||
/* global MessageFormatParser: false */
|
||||
/* global stringify: false */
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name $$messageFormat
|
||||
* @ngdoc module
|
||||
* @name ngMessageFormat
|
||||
* @packageName angular-message-format
|
||||
*
|
||||
* @description
|
||||
* Angular internal service to recognize MessageFormat extensions in interpolation expressions.
|
||||
* For more information, see:
|
||||
* https://docs.google.com/a/google.com/document/d/1pbtW2yvtmFBikfRrJd8VAsabiFkKezmYZ_PbgdjQOVU/edit
|
||||
*
|
||||
* ## Example
|
||||
* ## What is ngMessageFormat?
|
||||
*
|
||||
* <example name="ngMessageFormat-example" module="msgFmtExample" deps="angular-message-format.min.js">
|
||||
* The ngMessageFormat module extends the Angular {@link ng.$interpolate `$interpolate`} service
|
||||
* with a syntax for handling pluralization and gender specific messages, which is based on the
|
||||
* [ICU MessageFormat syntax][ICU].
|
||||
*
|
||||
* See [the design doc][ngMessageFormat doc] for more information.
|
||||
*
|
||||
* [ICU]: http://userguide.icu-project.org/formatparse/messages#TOC-MessageFormat
|
||||
* [ngMessageFormat doc]: https://docs.google.com/a/google.com/document/d/1pbtW2yvtmFBikfRrJd8VAsabiFkKezmYZ_PbgdjQOVU/edit
|
||||
*
|
||||
* ## Examples
|
||||
*
|
||||
* ### Gender
|
||||
*
|
||||
* This example uses the "select" keyword to specify the message based on gender.
|
||||
*
|
||||
* <example name="ngMessageFormat-example-gender" module="msgFmtExample" deps="angular-message-format.js">
|
||||
* <file name="index.html">
|
||||
* <div ng-controller="AppController">
|
||||
* Select Recipient:<br>
|
||||
<select ng-model="recipient" ng-options="person as person.name for person in recipients">
|
||||
</select>
|
||||
<p>{{recipient.gender, select,
|
||||
male {{{recipient.name}} unwrapped his gift. }
|
||||
female {{{recipient.name}} unwrapped her gift. }
|
||||
other {{{recipient.name}} unwrapped their gift. }
|
||||
}}</p>
|
||||
* </div>
|
||||
* </file>
|
||||
* <file name="script.js">
|
||||
* function Person(name, gender) {
|
||||
* this.name = name;
|
||||
* this.gender = gender;
|
||||
* }
|
||||
*
|
||||
* var alice = new Person("Alice", "female"),
|
||||
* bob = new Person("Bob", "male"),
|
||||
* ashley = new Person("Ashley", "");
|
||||
*
|
||||
* angular.module('msgFmtExample', ['ngMessageFormat'])
|
||||
* .controller('AppController', ['$scope', function($scope) {
|
||||
* $scope.recipients = [alice, bob, ashley];
|
||||
* $scope.recipient = $scope.recipients[0];
|
||||
* }]);
|
||||
* </file>
|
||||
* </example>
|
||||
*
|
||||
* ### Plural
|
||||
*
|
||||
* This example shows how the "plural" keyword is used to account for a variable number of entities.
|
||||
* The "#" variable holds the current number and can be embedded in the message.
|
||||
*
|
||||
* Note that "=1" takes precedence over "one".
|
||||
*
|
||||
* The example also shows the "offset" keyword, which allows you to offset the value of the "#" variable.
|
||||
*
|
||||
* <example name="ngMessageFormat-example-plural" module="msgFmtExample" deps="angular-message-format.js">
|
||||
* <file name="index.html">
|
||||
* <div ng-controller="AppController">
|
||||
* <button ng-click="decreaseRecipients()" id="decreaseRecipients">decreaseRecipients</button><br>
|
||||
* <span>{{recipients.length, plural, offset:1
|
||||
* <button ng-click="recipients.pop()" id="decreaseRecipients">decreaseRecipients</button><br>
|
||||
* Select recipients:<br>
|
||||
* <select multiple size=5 ng-model="recipients" ng-options="person as person.name for person in people">
|
||||
* </select><br>
|
||||
* <p>{{recipients.length, plural, offset:1
|
||||
* =0 {{{sender.name}} gave no gifts (\#=#)}
|
||||
* =1 {{{sender.name}} gave one gift to {{recipients[0].name}} (\#=#)}
|
||||
* =1 {{{sender.name}} gave a gift to {{recipients[0].name}} (\#=#)}
|
||||
* one {{{sender.name}} gave {{recipients[0].name}} and one other person a gift (\#=#)}
|
||||
* other {{{sender.name}} gave {{recipients[0].name}} and # other people a gift (\#=#)}
|
||||
* }}</span>
|
||||
* }}</p>
|
||||
* </div>
|
||||
* </file>
|
||||
*
|
||||
@@ -41,35 +100,79 @@
|
||||
*
|
||||
* var alice = new Person("Alice", "female"),
|
||||
* bob = new Person("Bob", "male"),
|
||||
* charlie = new Person("Charlie", "male"),
|
||||
* harry = new Person("Harry Potter", "male");
|
||||
* sarah = new Person("Sarah", "female"),
|
||||
* harry = new Person("Harry Potter", "male"),
|
||||
* ashley = new Person("Ashley", "");
|
||||
*
|
||||
* angular.module('msgFmtExample', ['ngMessageFormat'])
|
||||
* .controller('AppController', ['$scope', function($scope) {
|
||||
* $scope.recipients = [alice, bob, charlie];
|
||||
* $scope.people = [alice, bob, sarah, ashley];
|
||||
* $scope.recipients = [alice, bob, sarah];
|
||||
* $scope.sender = harry;
|
||||
* $scope.decreaseRecipients = function() {
|
||||
* --$scope.recipients.length;
|
||||
* };
|
||||
* }]);
|
||||
* </file>
|
||||
*
|
||||
* <file name="protractor.js" type="protractor">
|
||||
* describe('MessageFormat plural', function() {
|
||||
*
|
||||
* it('should pluralize initial values', function() {
|
||||
* var messageElem = element(by.binding('recipients.length')), decreaseRecipientsBtn = element(by.id('decreaseRecipients'));
|
||||
* var messageElem = element(by.binding('recipients.length')),
|
||||
* decreaseRecipientsBtn = element(by.id('decreaseRecipients'));
|
||||
*
|
||||
* expect(messageElem.getText()).toEqual('Harry Potter gave Alice and 2 other people a gift (#=2)');
|
||||
* decreaseRecipientsBtn.click();
|
||||
* expect(messageElem.getText()).toEqual('Harry Potter gave Alice and one other person a gift (#=1)');
|
||||
* decreaseRecipientsBtn.click();
|
||||
* expect(messageElem.getText()).toEqual('Harry Potter gave one gift to Alice (#=0)');
|
||||
* expect(messageElem.getText()).toEqual('Harry Potter gave a gift to Alice (#=0)');
|
||||
* decreaseRecipientsBtn.click();
|
||||
* expect(messageElem.getText()).toEqual('Harry Potter gave no gifts (#=-1)');
|
||||
* });
|
||||
* });
|
||||
* </file>
|
||||
* </example>
|
||||
*
|
||||
* ### Plural and Gender together
|
||||
*
|
||||
* This example shows how you can specify gender rules for specific plural matches - in this case,
|
||||
* =1 is special cased for gender.
|
||||
* <example name="ngMessageFormat-example-plural-gender" module="msgFmtExample" deps="angular-message-format.js">
|
||||
* <file name="index.html">
|
||||
* <div ng-controller="AppController">
|
||||
Select recipients:<br>
|
||||
<select multiple size=5 ng-model="recipients" ng-options="person as person.name for person in people">
|
||||
</select><br>
|
||||
<p>{{recipients.length, plural,
|
||||
=0 {{{sender.name}} has not given any gifts to anyone.}
|
||||
=1 { {{recipients[0].gender, select,
|
||||
female { {{sender.name}} gave {{recipients[0].name}} her gift.}
|
||||
male { {{sender.name}} gave {{recipients[0].name}} his gift.}
|
||||
other { {{sender.name}} gave {{recipients[0].name}} their gift.}
|
||||
}}
|
||||
}
|
||||
other {{{sender.name}} gave {{recipients.length}} people gifts.}
|
||||
}}</p>
|
||||
</file>
|
||||
* <file name="script.js">
|
||||
* function Person(name, gender) {
|
||||
* this.name = name;
|
||||
* this.gender = gender;
|
||||
* }
|
||||
*
|
||||
* var alice = new Person("Alice", "female"),
|
||||
* bob = new Person("Bob", "male"),
|
||||
* harry = new Person("Harry Potter", "male"),
|
||||
* ashley = new Person("Ashley", "");
|
||||
*
|
||||
* angular.module('msgFmtExample', ['ngMessageFormat'])
|
||||
* .controller('AppController', ['$scope', function($scope) {
|
||||
* $scope.people = [alice, bob, ashley];
|
||||
* $scope.recipients = [alice];
|
||||
* $scope.sender = harry;
|
||||
* }]);
|
||||
* </file>
|
||||
</example>
|
||||
*/
|
||||
|
||||
var $$MessageFormatFactory = ['$parse', '$locale', '$sce', '$exceptionHandler', function $$messageFormat(
|
||||
$parse, $locale, $sce, $exceptionHandler) {
|
||||
|
||||
@@ -107,15 +210,18 @@ var $$interpolateDecorator = ['$$messageFormat', '$delegate', function $$interpo
|
||||
return interpolate;
|
||||
}];
|
||||
|
||||
var $interpolateMinErr;
|
||||
var isFunction;
|
||||
var noop;
|
||||
var toJson;
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngMessageFormat
|
||||
* @packageName angular-message-format
|
||||
* @description
|
||||
*/
|
||||
var module = window['angular']['module']('ngMessageFormat', ['ng']);
|
||||
module['factory']('$$messageFormat', $$MessageFormatFactory);
|
||||
module['config'](['$provide', function($provide) {
|
||||
$interpolateMinErr = window['angular']['$interpolateMinErr'];
|
||||
isFunction = window['angular']['isFunction'];
|
||||
noop = window['angular']['noop'];
|
||||
toJson = window['angular']['toJson'];
|
||||
|
||||
$provide['decorator']('$interpolate', $$interpolateDecorator);
|
||||
}]);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../.jshintrc-base",
|
||||
"globals": {
|
||||
"window": false,
|
||||
|
||||
"angular": false
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
/* jshint ignore:start */
|
||||
// this code is in the core, but not in angular-messages.js
|
||||
var isArray = angular.isArray;
|
||||
var forEach = angular.forEach;
|
||||
var isString = angular.isString;
|
||||
var jqLite = angular.element;
|
||||
/* jshint ignore:end */
|
||||
var forEach;
|
||||
var isArray;
|
||||
var isString;
|
||||
var jqLite;
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
@@ -262,379 +259,396 @@ var jqLite = angular.element;
|
||||
*
|
||||
* {@link ngAnimate Click here} to learn how to use JavaScript animations or to learn more about ngAnimate.
|
||||
*/
|
||||
angular.module('ngMessages', [])
|
||||
angular.module('ngMessages', [], function initAngularHelpers() {
|
||||
// Access helpers from angular core.
|
||||
// Do it inside a `config` block to ensure `window.angular` is available.
|
||||
forEach = angular.forEach;
|
||||
isArray = angular.isArray;
|
||||
isString = angular.isString;
|
||||
jqLite = angular.element;
|
||||
})
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @module ngMessages
|
||||
* @name ngMessages
|
||||
* @restrict AE
|
||||
*
|
||||
* @description
|
||||
* `ngMessages` is a directive that is designed to show and hide messages based on the state
|
||||
* of a key/value object that it listens on. The directive itself complements error message
|
||||
* reporting with the `ngModel` $error object (which stores a key/value state of validation errors).
|
||||
*
|
||||
* `ngMessages` manages the state of internal messages within its container element. The internal
|
||||
* messages use the `ngMessage` directive and will be inserted/removed from the page depending
|
||||
* on if they're present within the key/value object. By default, only one message will be displayed
|
||||
* at a time and this depends on the prioritization of the messages within the template. (This can
|
||||
* be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.)
|
||||
*
|
||||
* A remote template can also be used to promote message reusability and messages can also be
|
||||
* overridden.
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-message="stringValue">...</ANY>
|
||||
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
|
||||
* <ANY ng-message-exp="expressionValue">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-message when="stringValue">...</ng-message>
|
||||
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
|
||||
* <ng-message when-exp="expressionValue">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* @param {string} ngMessages an angular expression evaluating to a key/value object
|
||||
* (this is typically the $error object on an ngModel instance).
|
||||
* @param {string=} ngMessagesMultiple|multiple when set, all messages will be displayed with true
|
||||
*
|
||||
* @example
|
||||
* <example name="ngMessages-directive" module="ngMessagesExample"
|
||||
* deps="angular-messages.js"
|
||||
* animations="true" fixBase="true">
|
||||
* <file name="index.html">
|
||||
* <form name="myForm">
|
||||
* <label>
|
||||
* Enter your name:
|
||||
* <input type="text"
|
||||
* name="myName"
|
||||
* ng-model="name"
|
||||
* ng-minlength="5"
|
||||
* ng-maxlength="20"
|
||||
* required />
|
||||
* </label>
|
||||
* <pre>myForm.myName.$error = {{ myForm.myName.$error | json }}</pre>
|
||||
*
|
||||
* <div ng-messages="myForm.myName.$error" style="color:maroon" role="alert">
|
||||
* <div ng-message="required">You did not enter a field</div>
|
||||
* <div ng-message="minlength">Your field is too short</div>
|
||||
* <div ng-message="maxlength">Your field is too long</div>
|
||||
* </div>
|
||||
* </form>
|
||||
* </file>
|
||||
* <file name="script.js">
|
||||
* angular.module('ngMessagesExample', ['ngMessages']);
|
||||
* </file>
|
||||
* </example>
|
||||
*/
|
||||
.directive('ngMessages', ['$animate', function($animate) {
|
||||
var ACTIVE_CLASS = 'ng-active';
|
||||
var INACTIVE_CLASS = 'ng-inactive';
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @module ngMessages
|
||||
* @name ngMessages
|
||||
* @restrict AE
|
||||
*
|
||||
* @description
|
||||
* `ngMessages` is a directive that is designed to show and hide messages based on the state
|
||||
* of a key/value object that it listens on. The directive itself complements error message
|
||||
* reporting with the `ngModel` $error object (which stores a key/value state of validation errors).
|
||||
*
|
||||
* `ngMessages` manages the state of internal messages within its container element. The internal
|
||||
* messages use the `ngMessage` directive and will be inserted/removed from the page depending
|
||||
* on if they're present within the key/value object. By default, only one message will be displayed
|
||||
* at a time and this depends on the prioritization of the messages within the template. (This can
|
||||
* be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.)
|
||||
*
|
||||
* A remote template can also be used to promote message reusability and messages can also be
|
||||
* overridden.
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-message="stringValue">...</ANY>
|
||||
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
|
||||
* <ANY ng-message-exp="expressionValue">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-message when="stringValue">...</ng-message>
|
||||
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
|
||||
* <ng-message when-exp="expressionValue">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* @param {string} ngMessages an angular expression evaluating to a key/value object
|
||||
* (this is typically the $error object on an ngModel instance).
|
||||
* @param {string=} ngMessagesMultiple|multiple when set, all messages will be displayed with true
|
||||
*
|
||||
* @example
|
||||
* <example name="ngMessages-directive" module="ngMessagesExample"
|
||||
* deps="angular-messages.js"
|
||||
* animations="true" fixBase="true">
|
||||
* <file name="index.html">
|
||||
* <form name="myForm">
|
||||
* <label>
|
||||
* Enter your name:
|
||||
* <input type="text"
|
||||
* name="myName"
|
||||
* ng-model="name"
|
||||
* ng-minlength="5"
|
||||
* ng-maxlength="20"
|
||||
* required />
|
||||
* </label>
|
||||
* <pre>myForm.myName.$error = {{ myForm.myName.$error | json }}</pre>
|
||||
*
|
||||
* <div ng-messages="myForm.myName.$error" style="color:maroon" role="alert">
|
||||
* <div ng-message="required">You did not enter a field</div>
|
||||
* <div ng-message="minlength">Your field is too short</div>
|
||||
* <div ng-message="maxlength">Your field is too long</div>
|
||||
* </div>
|
||||
* </form>
|
||||
* </file>
|
||||
* <file name="script.js">
|
||||
* angular.module('ngMessagesExample', ['ngMessages']);
|
||||
* </file>
|
||||
* </example>
|
||||
*/
|
||||
.directive('ngMessages', ['$animate', function($animate) {
|
||||
var ACTIVE_CLASS = 'ng-active';
|
||||
var INACTIVE_CLASS = 'ng-inactive';
|
||||
|
||||
return {
|
||||
require: 'ngMessages',
|
||||
restrict: 'AE',
|
||||
controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
|
||||
var ctrl = this;
|
||||
var latestKey = 0;
|
||||
var nextAttachId = 0;
|
||||
return {
|
||||
require: 'ngMessages',
|
||||
restrict: 'AE',
|
||||
controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
|
||||
var ctrl = this;
|
||||
var latestKey = 0;
|
||||
var nextAttachId = 0;
|
||||
|
||||
this.getAttachId = function getAttachId() { return nextAttachId++; };
|
||||
this.getAttachId = function getAttachId() { return nextAttachId++; };
|
||||
|
||||
var messages = this.messages = {};
|
||||
var renderLater, cachedCollection;
|
||||
var messages = this.messages = {};
|
||||
var renderLater, cachedCollection;
|
||||
|
||||
this.render = function(collection) {
|
||||
collection = collection || {};
|
||||
this.render = function(collection) {
|
||||
collection = collection || {};
|
||||
|
||||
renderLater = false;
|
||||
cachedCollection = collection;
|
||||
renderLater = false;
|
||||
cachedCollection = collection;
|
||||
|
||||
// this is true if the attribute is empty or if the attribute value is truthy
|
||||
var multiple = isAttrTruthy($scope, $attrs.ngMessagesMultiple) ||
|
||||
isAttrTruthy($scope, $attrs.multiple);
|
||||
// this is true if the attribute is empty or if the attribute value is truthy
|
||||
var multiple = isAttrTruthy($scope, $attrs.ngMessagesMultiple) ||
|
||||
isAttrTruthy($scope, $attrs.multiple);
|
||||
|
||||
var unmatchedMessages = [];
|
||||
var matchedKeys = {};
|
||||
var messageItem = ctrl.head;
|
||||
var messageFound = false;
|
||||
var totalMessages = 0;
|
||||
var unmatchedMessages = [];
|
||||
var matchedKeys = {};
|
||||
var messageItem = ctrl.head;
|
||||
var messageFound = false;
|
||||
var totalMessages = 0;
|
||||
|
||||
// we use != instead of !== to allow for both undefined and null values
|
||||
while (messageItem != null) {
|
||||
totalMessages++;
|
||||
var messageCtrl = messageItem.message;
|
||||
// we use != instead of !== to allow for both undefined and null values
|
||||
while (messageItem != null) {
|
||||
totalMessages++;
|
||||
var messageCtrl = messageItem.message;
|
||||
|
||||
var messageUsed = false;
|
||||
if (!messageFound) {
|
||||
forEach(collection, function(value, key) {
|
||||
if (!messageUsed && truthy(value) && messageCtrl.test(key)) {
|
||||
// this is to prevent the same error name from showing up twice
|
||||
if (matchedKeys[key]) return;
|
||||
matchedKeys[key] = true;
|
||||
var messageUsed = false;
|
||||
if (!messageFound) {
|
||||
forEach(collection, function(value, key) {
|
||||
if (!messageUsed && truthy(value) && messageCtrl.test(key)) {
|
||||
// this is to prevent the same error name from showing up twice
|
||||
if (matchedKeys[key]) return;
|
||||
matchedKeys[key] = true;
|
||||
|
||||
messageUsed = true;
|
||||
messageCtrl.attach();
|
||||
}
|
||||
});
|
||||
}
|
||||
messageUsed = true;
|
||||
messageCtrl.attach();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (messageUsed) {
|
||||
// unless we want to display multiple messages then we should
|
||||
// set a flag here to avoid displaying the next message in the list
|
||||
messageFound = !multiple;
|
||||
} else {
|
||||
unmatchedMessages.push(messageCtrl);
|
||||
}
|
||||
if (messageUsed) {
|
||||
// unless we want to display multiple messages then we should
|
||||
// set a flag here to avoid displaying the next message in the list
|
||||
messageFound = !multiple;
|
||||
} else {
|
||||
unmatchedMessages.push(messageCtrl);
|
||||
}
|
||||
|
||||
messageItem = messageItem.next;
|
||||
}
|
||||
messageItem = messageItem.next;
|
||||
}
|
||||
|
||||
forEach(unmatchedMessages, function(messageCtrl) {
|
||||
messageCtrl.detach();
|
||||
});
|
||||
forEach(unmatchedMessages, function(messageCtrl) {
|
||||
messageCtrl.detach();
|
||||
});
|
||||
|
||||
unmatchedMessages.length !== totalMessages
|
||||
unmatchedMessages.length !== totalMessages
|
||||
? $animate.setClass($element, ACTIVE_CLASS, INACTIVE_CLASS)
|
||||
: $animate.setClass($element, INACTIVE_CLASS, ACTIVE_CLASS);
|
||||
};
|
||||
};
|
||||
|
||||
$scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
|
||||
$scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
|
||||
|
||||
// If the element is destroyed, proactively destroy all the currently visible messages
|
||||
$element.on('$destroy', function() {
|
||||
forEach(messages, function(item) {
|
||||
item.message.detach();
|
||||
});
|
||||
});
|
||||
// If the element is destroyed, proactively destroy all the currently visible messages
|
||||
$element.on('$destroy', function() {
|
||||
forEach(messages, function(item) {
|
||||
item.message.detach();
|
||||
});
|
||||
});
|
||||
|
||||
this.reRender = function() {
|
||||
if (!renderLater) {
|
||||
renderLater = true;
|
||||
$scope.$evalAsync(function() {
|
||||
if (renderLater) {
|
||||
cachedCollection && ctrl.render(cachedCollection);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
this.reRender = function() {
|
||||
if (!renderLater) {
|
||||
renderLater = true;
|
||||
$scope.$evalAsync(function() {
|
||||
if (renderLater) {
|
||||
cachedCollection && ctrl.render(cachedCollection);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.register = function(comment, messageCtrl) {
|
||||
var nextKey = latestKey.toString();
|
||||
messages[nextKey] = {
|
||||
message: messageCtrl
|
||||
};
|
||||
insertMessageNode($element[0], comment, nextKey);
|
||||
comment.$$ngMessageNode = nextKey;
|
||||
latestKey++;
|
||||
this.register = function(comment, messageCtrl) {
|
||||
var nextKey = latestKey.toString();
|
||||
messages[nextKey] = {
|
||||
message: messageCtrl
|
||||
};
|
||||
insertMessageNode($element[0], comment, nextKey);
|
||||
comment.$$ngMessageNode = nextKey;
|
||||
latestKey++;
|
||||
|
||||
ctrl.reRender();
|
||||
};
|
||||
ctrl.reRender();
|
||||
};
|
||||
|
||||
this.deregister = function(comment) {
|
||||
var key = comment.$$ngMessageNode;
|
||||
delete comment.$$ngMessageNode;
|
||||
removeMessageNode($element[0], comment, key);
|
||||
delete messages[key];
|
||||
ctrl.reRender();
|
||||
};
|
||||
this.deregister = function(comment) {
|
||||
var key = comment.$$ngMessageNode;
|
||||
delete comment.$$ngMessageNode;
|
||||
removeMessageNode($element[0], comment, key);
|
||||
delete messages[key];
|
||||
ctrl.reRender();
|
||||
};
|
||||
|
||||
function findPreviousMessage(parent, comment) {
|
||||
var prevNode = comment;
|
||||
var parentLookup = [];
|
||||
function findPreviousMessage(parent, comment) {
|
||||
var prevNode = comment;
|
||||
var parentLookup = [];
|
||||
|
||||
while (prevNode && prevNode !== parent) {
|
||||
var prevKey = prevNode.$$ngMessageNode;
|
||||
if (prevKey && prevKey.length) {
|
||||
return messages[prevKey];
|
||||
}
|
||||
while (prevNode && prevNode !== parent) {
|
||||
var prevKey = prevNode.$$ngMessageNode;
|
||||
if (prevKey && prevKey.length) {
|
||||
return messages[prevKey];
|
||||
}
|
||||
|
||||
// dive deeper into the DOM and examine its children for any ngMessage
|
||||
// comments that may be in an element that appears deeper in the list
|
||||
if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) == -1) {
|
||||
parentLookup.push(prevNode);
|
||||
prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
|
||||
} else if (prevNode.previousSibling) {
|
||||
prevNode = prevNode.previousSibling;
|
||||
} else {
|
||||
prevNode = prevNode.parentNode;
|
||||
parentLookup.push(prevNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
// dive deeper into the DOM and examine its children for any ngMessage
|
||||
// comments that may be in an element that appears deeper in the list
|
||||
if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) === -1) {
|
||||
parentLookup.push(prevNode);
|
||||
prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
|
||||
} else if (prevNode.previousSibling) {
|
||||
prevNode = prevNode.previousSibling;
|
||||
} else {
|
||||
prevNode = prevNode.parentNode;
|
||||
parentLookup.push(prevNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function insertMessageNode(parent, comment, key) {
|
||||
var messageNode = messages[key];
|
||||
if (!ctrl.head) {
|
||||
ctrl.head = messageNode;
|
||||
} else {
|
||||
var match = findPreviousMessage(parent, comment);
|
||||
if (match) {
|
||||
messageNode.next = match.next;
|
||||
match.next = messageNode;
|
||||
} else {
|
||||
messageNode.next = ctrl.head;
|
||||
ctrl.head = messageNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
function insertMessageNode(parent, comment, key) {
|
||||
var messageNode = messages[key];
|
||||
if (!ctrl.head) {
|
||||
ctrl.head = messageNode;
|
||||
} else {
|
||||
var match = findPreviousMessage(parent, comment);
|
||||
if (match) {
|
||||
messageNode.next = match.next;
|
||||
match.next = messageNode;
|
||||
} else {
|
||||
messageNode.next = ctrl.head;
|
||||
ctrl.head = messageNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeMessageNode(parent, comment, key) {
|
||||
var messageNode = messages[key];
|
||||
function removeMessageNode(parent, comment, key) {
|
||||
var messageNode = messages[key];
|
||||
|
||||
var match = findPreviousMessage(parent, comment);
|
||||
if (match) {
|
||||
match.next = messageNode.next;
|
||||
} else {
|
||||
ctrl.head = messageNode.next;
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
var match = findPreviousMessage(parent, comment);
|
||||
if (match) {
|
||||
match.next = messageNode.next;
|
||||
} else {
|
||||
ctrl.head = messageNode.next;
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
function isAttrTruthy(scope, attr) {
|
||||
return (isString(attr) && attr.length === 0) || //empty attribute
|
||||
truthy(scope.$eval(attr));
|
||||
}
|
||||
function isAttrTruthy(scope, attr) {
|
||||
return (isString(attr) && attr.length === 0) || //empty attribute
|
||||
truthy(scope.$eval(attr));
|
||||
}
|
||||
|
||||
function truthy(val) {
|
||||
return isString(val) ? val.length : !!val;
|
||||
}
|
||||
}])
|
||||
function truthy(val) {
|
||||
return isString(val) ? val.length : !!val;
|
||||
}
|
||||
}])
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessagesInclude
|
||||
* @restrict AE
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessagesInclude` is a directive with the purpose to import existing ngMessage template
|
||||
* code from a remote template and place the downloaded template code into the exact spot
|
||||
* that the ngMessagesInclude directive is placed within the ngMessages container. This allows
|
||||
* for a series of pre-defined messages to be reused and also allows for the developer to
|
||||
* determine what messages are overridden due to the placement of the ngMessagesInclude directive.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-messages-include="remoteTplString">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-messages-include src="expressionValue1">...</ng-messages-include>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @param {string} ngMessagesInclude|src a string value corresponding to the remote template.
|
||||
*/
|
||||
.directive('ngMessagesInclude',
|
||||
['$templateRequest', '$document', '$compile', function($templateRequest, $document, $compile) {
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessagesInclude
|
||||
* @restrict AE
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessagesInclude` is a directive with the purpose to import existing ngMessage template
|
||||
* code from a remote template and place the downloaded template code into the exact spot
|
||||
* that the ngMessagesInclude directive is placed within the ngMessages container. This allows
|
||||
* for a series of pre-defined messages to be reused and also allows for the developer to
|
||||
* determine what messages are overridden due to the placement of the ngMessagesInclude directive.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-messages-include="remoteTplString">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-messages-include src="expressionValue1">...</ng-messages-include>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @param {string} ngMessagesInclude|src a string value corresponding to the remote template.
|
||||
*/
|
||||
.directive('ngMessagesInclude',
|
||||
['$templateRequest', '$document', '$compile', function($templateRequest, $document, $compile) {
|
||||
|
||||
return {
|
||||
restrict: 'AE',
|
||||
require: '^^ngMessages', // we only require this for validation sake
|
||||
link: function($scope, element, attrs) {
|
||||
var src = attrs.ngMessagesInclude || attrs.src;
|
||||
$templateRequest(src).then(function(html) {
|
||||
if ($scope.$$destroyed) return;
|
||||
return {
|
||||
restrict: 'AE',
|
||||
require: '^^ngMessages', // we only require this for validation sake
|
||||
link: function($scope, element, attrs) {
|
||||
var src = attrs.ngMessagesInclude || attrs.src;
|
||||
$templateRequest(src).then(function(html) {
|
||||
if ($scope.$$destroyed) return;
|
||||
|
||||
$compile(html)($scope, function(contents) {
|
||||
element.after(contents);
|
||||
if (isString(html) && !html.trim()) {
|
||||
// Empty template - nothing to compile
|
||||
replaceElementWithMarker(element, src);
|
||||
} else {
|
||||
// Non-empty template - compile and link
|
||||
$compile(html)($scope, function(contents) {
|
||||
element.after(contents);
|
||||
replaceElementWithMarker(element, src);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// the anchor is placed for debugging purposes
|
||||
var comment = $compile.$$createComment ?
|
||||
$compile.$$createComment('ngMessagesInclude', src) :
|
||||
$document[0].createComment(' ngMessagesInclude: ' + src + ' ');
|
||||
var anchor = jqLite(comment);
|
||||
element.after(anchor);
|
||||
// Helpers
|
||||
function replaceElementWithMarker(element, src) {
|
||||
// A comment marker is placed for debugging purposes
|
||||
var comment = $compile.$$createComment ?
|
||||
$compile.$$createComment('ngMessagesInclude', src) :
|
||||
$document[0].createComment(' ngMessagesInclude: ' + src + ' ');
|
||||
var marker = jqLite(comment);
|
||||
element.after(marker);
|
||||
|
||||
// we don't want to pollute the DOM anymore by keeping an empty directive element
|
||||
element.remove();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}])
|
||||
// Don't pollute the DOM anymore by keeping an empty directive element
|
||||
element.remove();
|
||||
}
|
||||
}])
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessage
|
||||
* @restrict AE
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessage` is a directive with the purpose to show and hide a particular message.
|
||||
* For `ngMessage` to operate, a parent `ngMessages` directive on a parent DOM element
|
||||
* must be situated since it determines which messages are visible based on the state
|
||||
* of the provided key/value map that `ngMessages` listens on.
|
||||
*
|
||||
* More information about using `ngMessage` can be found in the
|
||||
* {@link module:ngMessages `ngMessages` module documentation}.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-message="stringValue">...</ANY>
|
||||
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-message when="stringValue">...</ng-message>
|
||||
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* @param {expression} ngMessage|when a string value corresponding to the message key.
|
||||
*/
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessage
|
||||
* @restrict AE
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessage` is a directive with the purpose to show and hide a particular message.
|
||||
* For `ngMessage` to operate, a parent `ngMessages` directive on a parent DOM element
|
||||
* must be situated since it determines which messages are visible based on the state
|
||||
* of the provided key/value map that `ngMessages` listens on.
|
||||
*
|
||||
* More information about using `ngMessage` can be found in the
|
||||
* {@link module:ngMessages `ngMessages` module documentation}.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression" role="alert">
|
||||
* <ANY ng-message="stringValue">...</ANY>
|
||||
* <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression" role="alert">
|
||||
* <ng-message when="stringValue">...</ng-message>
|
||||
* <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* @param {expression} ngMessage|when a string value corresponding to the message key.
|
||||
*/
|
||||
.directive('ngMessage', ngMessageDirectiveFactory())
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessageExp
|
||||
* @restrict AE
|
||||
* @priority 1
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessageExp` is a directive with the purpose to show and hide a particular message.
|
||||
* For `ngMessageExp` to operate, a parent `ngMessages` directive on a parent DOM element
|
||||
* must be situated since it determines which messages are visible based on the state
|
||||
* of the provided key/value map that `ngMessages` listens on.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression">
|
||||
* <ANY ng-message-exp="expressionValue">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression">
|
||||
* <ng-message when-exp="expressionValue">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @param {expression} ngMessageExp|whenExp an expression value corresponding to the message key.
|
||||
*/
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngMessageExp
|
||||
* @restrict AE
|
||||
* @priority 1
|
||||
* @scope
|
||||
*
|
||||
* @description
|
||||
* `ngMessageExp` is a directive with the purpose to show and hide a particular message.
|
||||
* For `ngMessageExp` to operate, a parent `ngMessages` directive on a parent DOM element
|
||||
* must be situated since it determines which messages are visible based on the state
|
||||
* of the provided key/value map that `ngMessages` listens on.
|
||||
*
|
||||
* @usage
|
||||
* ```html
|
||||
* <!-- using attribute directives -->
|
||||
* <ANY ng-messages="expression">
|
||||
* <ANY ng-message-exp="expressionValue">...</ANY>
|
||||
* </ANY>
|
||||
*
|
||||
* <!-- or by using element directives -->
|
||||
* <ng-messages for="expression">
|
||||
* <ng-message when-exp="expressionValue">...</ng-message>
|
||||
* </ng-messages>
|
||||
* ```
|
||||
*
|
||||
* {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
|
||||
*
|
||||
* @param {expression} ngMessageExp|whenExp an expression value corresponding to the message key.
|
||||
*/
|
||||
.directive('ngMessageExp', ngMessageDirectiveFactory());
|
||||
|
||||
function ngMessageDirectiveFactory() {
|
||||
@@ -654,8 +668,8 @@ function ngMessageDirectiveFactory() {
|
||||
var assignRecords = function(items) {
|
||||
records = items
|
||||
? (isArray(items)
|
||||
? items
|
||||
: items.split(/[\s,]+/))
|
||||
? items
|
||||
: items.split(/[\s,]+/))
|
||||
: null;
|
||||
ngMessagesCtrl.reRender();
|
||||
};
|
||||
@@ -674,7 +688,7 @@ function ngMessageDirectiveFactory() {
|
||||
},
|
||||
attach: function() {
|
||||
if (!currentElement) {
|
||||
$transclude(scope, function(elm) {
|
||||
$transclude(function(elm, newScope) {
|
||||
$animate.enter(elm, null, element);
|
||||
currentElement = elm;
|
||||
|
||||
@@ -690,6 +704,7 @@ function ngMessageDirectiveFactory() {
|
||||
ngMessagesCtrl.deregister(commentNode);
|
||||
messageCtrl.detach();
|
||||
}
|
||||
newScope.$destroy();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -106,19 +106,29 @@ angular.mock.$Browser = function() {
|
||||
* @param {number=} number of milliseconds to flush. See {@link #defer.now}
|
||||
*/
|
||||
self.defer.flush = function(delay) {
|
||||
var nextTime;
|
||||
|
||||
if (angular.isDefined(delay)) {
|
||||
self.defer.now += delay;
|
||||
// A delay was passed so compute the next time
|
||||
nextTime = self.defer.now + delay;
|
||||
} else {
|
||||
if (self.deferredFns.length) {
|
||||
self.defer.now = self.deferredFns[self.deferredFns.length - 1].time;
|
||||
// No delay was passed so set the next time so that it clears the deferred queue
|
||||
nextTime = self.deferredFns[self.deferredFns.length - 1].time;
|
||||
} else {
|
||||
// No delay passed, but there are no deferred tasks so flush - indicates an error!
|
||||
throw new Error('No deferred tasks to be flushed');
|
||||
}
|
||||
}
|
||||
|
||||
while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) {
|
||||
while (self.deferredFns.length && self.deferredFns[0].time <= nextTime) {
|
||||
// Increment the time and call the next deferred function
|
||||
self.defer.now = self.deferredFns[0].time;
|
||||
self.deferredFns.shift().fn();
|
||||
}
|
||||
|
||||
// Ensure that the current time is correct
|
||||
self.defer.now = nextTime;
|
||||
};
|
||||
|
||||
self.$$baseHref = '/';
|
||||
@@ -927,13 +937,10 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
|
||||
* @name angular.mock.dump
|
||||
* @description
|
||||
*
|
||||
* *NOTE*: this is not an injectable instance, just a globally available function.
|
||||
* *NOTE*: This is not an injectable instance, just a globally available function.
|
||||
*
|
||||
* Method for serializing common angular objects (scope, elements, etc..) into strings, useful for
|
||||
* debugging.
|
||||
*
|
||||
* This method is also available on window, where it can be used to display objects on debug
|
||||
* console.
|
||||
* Method for serializing common angular objects (scope, elements, etc..) into strings.
|
||||
* It is useful for logging objects to the console when debugging.
|
||||
*
|
||||
* @param {*} object - any object to turn into string.
|
||||
* @return {string} a serialized string of the argument
|
||||
@@ -2194,9 +2201,15 @@ angular.mock.$RootElementProvider = function() {
|
||||
angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
|
||||
return function(expression, locals, later, ident) {
|
||||
if (later && typeof later === 'object') {
|
||||
var create = $delegate(expression, locals, true, ident);
|
||||
angular.extend(create.instance, later);
|
||||
return create();
|
||||
var instantiate = $delegate(expression, locals, true, ident);
|
||||
angular.extend(instantiate.instance, later);
|
||||
|
||||
var instance = instantiate();
|
||||
if (instance !== instantiate.instance) {
|
||||
angular.extend(instance, later);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
return $delegate(expression, locals, later, ident);
|
||||
};
|
||||
@@ -2268,7 +2281,7 @@ angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compi
|
||||
* * [Google CDN](https://developers.google.com/speed/libraries/devguide#angularjs) e.g.
|
||||
* `"//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/angular-mocks.js"`
|
||||
* * [NPM](https://www.npmjs.com/) e.g. `npm install angular-mocks@X.Y.Z`
|
||||
* * [Bower](http://bower.io) e.g. `bower install angular-mocks@X.Y.Z`
|
||||
* * [Bower](http://bower.io) e.g. `bower install angular-mocks#X.Y.Z`
|
||||
* * [code.angularjs.org](https://code.angularjs.org/) (discouraged for production use) e.g.
|
||||
* `"//code.angularjs.org/X.Y.Z/angular-mocks.js"`
|
||||
*
|
||||
@@ -2318,6 +2331,7 @@ angular.module('ngMock', ['ng']).provider({
|
||||
* the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock.
|
||||
*/
|
||||
angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
||||
$provide.value('$httpBackend', angular.injector(['ng']).get('$httpBackend'));
|
||||
$provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator);
|
||||
}]);
|
||||
|
||||
@@ -2916,7 +2930,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
||||
angular.forEach(angular.callbacks, function(val, key) {
|
||||
delete angular.callbacks[key];
|
||||
});
|
||||
angular.callbacks.counter = 0;
|
||||
angular.callbacks.$$counter = 0;
|
||||
};
|
||||
|
||||
(window.beforeEach || window.setup)(module.$$beforeEach);
|
||||
@@ -3019,7 +3033,7 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
|
||||
this.stack = e.stack + '\n' + errorForStack.stack;
|
||||
if (e.stackArray) this.stackArray = e.stackArray;
|
||||
};
|
||||
ErrorAddingDeclarationLocationStack.prototype.toString = Error.prototype.toString;
|
||||
ErrorAddingDeclarationLocationStack.prototype = Error.prototype;
|
||||
|
||||
window.inject = angular.mock.inject = function() {
|
||||
var blockFns = Array.prototype.slice.call(arguments, 0);
|
||||
|
||||
@@ -56,7 +56,21 @@ function shallowClearAndCopy(src, dst) {
|
||||
*
|
||||
* <div doc-module-components="ngResource"></div>
|
||||
*
|
||||
* See {@link ngResource.$resource `$resource`} for usage.
|
||||
* See {@link ngResource.$resourceProvider} and {@link ngResource.$resource} for usage.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc provider
|
||||
* @name $resourceProvider
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* Use `$resourceProvider` to change the default behavior of the {@link ngResource.$resource}
|
||||
* service.
|
||||
*
|
||||
* ## Dependencies
|
||||
* Requires the {@link ngResource } module to be installed.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -100,8 +114,9 @@ function shallowClearAndCopy(src, dst) {
|
||||
* can escape it with `/\.`.
|
||||
*
|
||||
* @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
|
||||
* `actions` methods. If a parameter value is a function, it will be executed every time
|
||||
* when a param value needs to be obtained for a request (unless the param was overridden).
|
||||
* `actions` methods. If a parameter value is a function, it will be called every time
|
||||
* a param value needs to be obtained for a request (unless the param was overridden). The function
|
||||
* will be passed the current data value as an argument.
|
||||
*
|
||||
* Each key value in the parameter object is first bound to url template if present and then any
|
||||
* excess keys are appended to the url search query after the `?`.
|
||||
@@ -109,10 +124,13 @@ function shallowClearAndCopy(src, dst) {
|
||||
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
|
||||
* URL `/path/greet?salutation=Hello`.
|
||||
*
|
||||
* If the parameter value is prefixed with `@` then the value for that parameter will be extracted
|
||||
* from the corresponding property on the `data` object (provided when calling an action method).
|
||||
* If the parameter value is prefixed with `@`, then the value for that parameter will be
|
||||
* extracted from the corresponding property on the `data` object (provided when calling a
|
||||
* "non-GET" action method).
|
||||
* For example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of
|
||||
* `someParam` will be `data.someProp`.
|
||||
* Note that the parameter will be ignored, when calling a "GET" action method (i.e. an action
|
||||
* method that does not accept a request body)
|
||||
*
|
||||
* @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
|
||||
* the default set of resource actions. The declaration should be created in the format of {@link
|
||||
@@ -129,8 +147,9 @@ function shallowClearAndCopy(src, dst) {
|
||||
* - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
|
||||
* `DELETE`, `JSONP`, etc).
|
||||
* - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
|
||||
* the parameter value is a function, it will be executed every time when a param value needs to
|
||||
* be obtained for a request (unless the param was overridden).
|
||||
* the parameter value is a function, it will be called every time when a param value needs to
|
||||
* be obtained for a request (unless the param was overridden). The function will be passed the
|
||||
* current data value as an argument.
|
||||
* - **`url`** – {string} – action specific `url` override. The url templating is supported just
|
||||
* like for the resource-level urls.
|
||||
* - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
|
||||
@@ -251,6 +270,14 @@ function shallowClearAndCopy(src, dst) {
|
||||
* - `$cancelRequest`: If there is a cancellable, pending request related to the instance or
|
||||
* collection, calling this method will abort the request.
|
||||
*
|
||||
* The Resource instances have these additional methods:
|
||||
*
|
||||
* - `toJSON`: It returns a simple object without any of the extra properties added as part of
|
||||
* the Resource API. This object can be serialized through {@link angular.toJson} safely
|
||||
* without attaching Angular-specific fields. Notice that `JSON.stringify` (and
|
||||
* `angular.toJson`) automatically use this method when serializing a Resource instance
|
||||
* (see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior)).
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* # Credit card resource
|
||||
@@ -402,10 +429,78 @@ angular.module('ngResource', ['ng']).
|
||||
var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/;
|
||||
var provider = this;
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $resourceProvider#defaults
|
||||
* @description
|
||||
* Object containing default options used when creating `$resource` instances.
|
||||
*
|
||||
* The default values satisfy a wide range of usecases, but you may choose to overwrite any of
|
||||
* them to further customize your instances. The available properties are:
|
||||
*
|
||||
* - **stripTrailingSlashes** – `{boolean}` – If true, then the trailing slashes from any
|
||||
* calculated URL will be stripped.<br />
|
||||
* (Defaults to true.)
|
||||
* - **cancellable** – `{boolean}` – If true, the request made by a "non-instance" call will be
|
||||
* cancelled (if not already completed) by calling `$cancelRequest()` on the call's return
|
||||
* value. For more details, see {@link ngResource.$resource}. This can be overwritten per
|
||||
* resource class or action.<br />
|
||||
* (Defaults to false.)
|
||||
* - **actions** - `{Object.<Object>}` - A hash with default actions declarations. Actions are
|
||||
* high-level methods corresponding to RESTful actions/methods on resources. An action may
|
||||
* specify what HTTP method to use, what URL to hit, if the return value will be a single
|
||||
* object or a collection (array) of objects etc. For more details, see
|
||||
* {@link ngResource.$resource}. The actions can also be enhanced or overwritten per resource
|
||||
* class.<br />
|
||||
* The default actions are:
|
||||
* ```js
|
||||
* {
|
||||
* get: {method: 'GET'},
|
||||
* save: {method: 'POST'},
|
||||
* query: {method: 'GET', isArray: true},
|
||||
* remove: {method: 'DELETE'},
|
||||
* delete: {method: 'DELETE'}
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* #### Example
|
||||
*
|
||||
* For example, you can specify a new `update` action that uses the `PUT` HTTP verb:
|
||||
*
|
||||
* ```js
|
||||
* angular.
|
||||
* module('myApp').
|
||||
* config(['resourceProvider', function ($resourceProvider) {
|
||||
* $resourceProvider.defaults.actions.update = {
|
||||
* method: 'PUT'
|
||||
* };
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Or you can even overwrite the whole `actions` list and specify your own:
|
||||
*
|
||||
* ```js
|
||||
* angular.
|
||||
* module('myApp').
|
||||
* config(['resourceProvider', function ($resourceProvider) {
|
||||
* $resourceProvider.defaults.actions = {
|
||||
* create: {method: 'POST'}
|
||||
* get: {method: 'GET'},
|
||||
* getAll: {method: 'GET', isArray:true},
|
||||
* update: {method: 'PUT'},
|
||||
* delete: {method: 'DELETE'}
|
||||
* };
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
this.defaults = {
|
||||
// Strip slashes by default
|
||||
stripTrailingSlashes: true,
|
||||
|
||||
// Make non-instance requests cancellable (via `$cancelRequest()`)
|
||||
cancellable: false,
|
||||
|
||||
// Default actions configuration
|
||||
actions: {
|
||||
'get': {method: 'GET'},
|
||||
@@ -551,7 +646,7 @@ angular.module('ngResource', ['ng']).
|
||||
var ids = {};
|
||||
actionParams = extend({}, paramDefaults, actionParams);
|
||||
forEach(actionParams, function(value, key) {
|
||||
if (isFunction(value)) { value = value(); }
|
||||
if (isFunction(value)) { value = value(data); }
|
||||
ids[key] = value && value.charAt && value.charAt(0) == '@' ?
|
||||
lookupDottedPath(data, value.substr(1)) : value;
|
||||
});
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
/* global shallowCopy: false */
|
||||
|
||||
// There are necessary for `shallowCopy()` (included via `src/shallowCopy.js`).
|
||||
// They are initialized inside the `$RouteProvider`, to ensure `window.angular` is available.
|
||||
var isArray;
|
||||
var isObject;
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
* @name ngRoute
|
||||
@@ -35,6 +42,9 @@ var ngRouteModule = angular.module('ngRoute', ['ng']).
|
||||
* Requires the {@link ngRoute `ngRoute`} module to be installed.
|
||||
*/
|
||||
function $RouteProvider() {
|
||||
isArray = angular.isArray;
|
||||
isObject = angular.isObject;
|
||||
|
||||
function inherit(parent, extra) {
|
||||
return angular.extend(Object.create(parent), extra);
|
||||
}
|
||||
@@ -154,7 +164,7 @@ function $RouteProvider() {
|
||||
*/
|
||||
this.when = function(path, route) {
|
||||
//copy original route object to preserve params inherited from proto chain
|
||||
var routeCopy = angular.copy(route);
|
||||
var routeCopy = shallowCopy(route);
|
||||
if (angular.isUndefined(routeCopy.reloadOnSearch)) {
|
||||
routeCopy.reloadOnSearch = true;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
"window": false,
|
||||
|
||||
"angular": false,
|
||||
"htmlSanitizeWriter": false
|
||||
"sanitizeText": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
/* global sanitizeText: false */
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name linky
|
||||
@@ -135,12 +133,20 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
MAILTO_REGEXP = /^mailto:/i;
|
||||
|
||||
var linkyMinErr = angular.$$minErr('linky');
|
||||
var isDefined = angular.isDefined;
|
||||
var isFunction = angular.isFunction;
|
||||
var isObject = angular.isObject;
|
||||
var isString = angular.isString;
|
||||
|
||||
return function(text, target, attributes) {
|
||||
if (text == null || text === '') return text;
|
||||
if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text);
|
||||
|
||||
var attributesFn =
|
||||
isFunction(attributes) ? attributes :
|
||||
isObject(attributes) ? function getAttributesObject() {return attributes;} :
|
||||
function getEmptyAttributesObject() {return {};};
|
||||
|
||||
var match;
|
||||
var raw = text;
|
||||
var html = [];
|
||||
@@ -169,19 +175,14 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
}
|
||||
|
||||
function addLink(url, text) {
|
||||
var key;
|
||||
var key, linkAttributes = attributesFn(url);
|
||||
html.push('<a ');
|
||||
if (angular.isFunction(attributes)) {
|
||||
attributes = attributes(url);
|
||||
|
||||
for (key in linkAttributes) {
|
||||
html.push(key + '="' + linkAttributes[key] + '" ');
|
||||
}
|
||||
if (angular.isObject(attributes)) {
|
||||
for (key in attributes) {
|
||||
html.push(key + '="' + attributes[key] + '" ');
|
||||
}
|
||||
} else {
|
||||
attributes = {};
|
||||
}
|
||||
if (angular.isDefined(target) && !('target' in attributes)) {
|
||||
|
||||
if (isDefined(target) && !('target' in linkAttributes)) {
|
||||
html.push('target="',
|
||||
target,
|
||||
'" ');
|
||||
|
||||
@@ -12,6 +12,14 @@
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
var $sanitizeMinErr = angular.$$minErr('$sanitize');
|
||||
var bind;
|
||||
var extend;
|
||||
var forEach;
|
||||
var isDefined;
|
||||
var lowercase;
|
||||
var noop;
|
||||
var htmlParser;
|
||||
var htmlSanitizeWriter;
|
||||
|
||||
/**
|
||||
* @ngdoc module
|
||||
@@ -144,7 +152,7 @@ function $SanitizeProvider() {
|
||||
|
||||
this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
|
||||
if (svgEnabled) {
|
||||
angular.extend(validElements, svgElements);
|
||||
extend(validElements, svgElements);
|
||||
}
|
||||
return function(html) {
|
||||
var buf = [];
|
||||
@@ -187,328 +195,340 @@ function $SanitizeProvider() {
|
||||
* without an argument or self for chaining otherwise.
|
||||
*/
|
||||
this.enableSvg = function(enableSvg) {
|
||||
if (angular.isDefined(enableSvg)) {
|
||||
if (isDefined(enableSvg)) {
|
||||
svgEnabled = enableSvg;
|
||||
return this;
|
||||
} else {
|
||||
return svgEnabled;
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private stuff
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bind = angular.bind;
|
||||
extend = angular.extend;
|
||||
forEach = angular.forEach;
|
||||
isDefined = angular.isDefined;
|
||||
lowercase = angular.lowercase;
|
||||
noop = angular.noop;
|
||||
|
||||
htmlParser = htmlParserImpl;
|
||||
htmlSanitizeWriter = htmlSanitizeWriterImpl;
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
|
||||
// Match everything outside of normal chars and " (quote character)
|
||||
NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;
|
||||
|
||||
|
||||
// Good source of info about elements and attributes
|
||||
// http://dev.w3.org/html5/spec/Overview.html#semantics
|
||||
// http://simon.html5.org/html-elements
|
||||
|
||||
// Safe Void Elements - HTML5
|
||||
// http://dev.w3.org/html5/spec/Overview.html#void-elements
|
||||
var voidElements = toMap("area,br,col,hr,img,wbr");
|
||||
|
||||
// Elements that you can, intentionally, leave open (and which close themselves)
|
||||
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
|
||||
var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
|
||||
optionalEndTagInlineElements = toMap("rp,rt"),
|
||||
optionalEndTagElements = extend({},
|
||||
optionalEndTagInlineElements,
|
||||
optionalEndTagBlockElements);
|
||||
|
||||
// Safe Block Elements - HTML5
|
||||
var blockElements = extend({}, optionalEndTagBlockElements, toMap("address,article," +
|
||||
"aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
|
||||
"h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul"));
|
||||
|
||||
// Inline Elements - HTML5
|
||||
var inlineElements = extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," +
|
||||
"bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
|
||||
"samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
|
||||
|
||||
// SVG Elements
|
||||
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
|
||||
// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
|
||||
// They can potentially allow for arbitrary javascript to be executed. See #11290
|
||||
var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
|
||||
"hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
|
||||
"radialGradient,rect,stop,svg,switch,text,title,tspan");
|
||||
|
||||
// Blocked Elements (will be stripped)
|
||||
var blockedElements = toMap("script,style");
|
||||
|
||||
var validElements = extend({},
|
||||
voidElements,
|
||||
blockElements,
|
||||
inlineElements,
|
||||
optionalEndTagElements);
|
||||
|
||||
//Attributes that have href and hence need to be sanitized
|
||||
var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href");
|
||||
|
||||
var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
|
||||
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
|
||||
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
|
||||
'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
|
||||
'valign,value,vspace,width');
|
||||
|
||||
// SVG attributes (without "id" and "name" attributes)
|
||||
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
|
||||
var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
|
||||
'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
|
||||
'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
|
||||
'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
|
||||
'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
|
||||
'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
|
||||
'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
|
||||
'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
|
||||
'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
|
||||
'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
|
||||
'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
|
||||
'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
|
||||
'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
|
||||
'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
|
||||
'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
|
||||
|
||||
var validAttrs = extend({},
|
||||
uriAttrs,
|
||||
svgAttrs,
|
||||
htmlAttrs);
|
||||
|
||||
function toMap(str, lowercaseKeys) {
|
||||
var obj = {}, items = str.split(','), i;
|
||||
for (i = 0; i < items.length; i++) {
|
||||
obj[lowercaseKeys ? lowercase(items[i]) : items[i]] = true;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
var inertBodyElement;
|
||||
(function(window) {
|
||||
var doc;
|
||||
if (window.document && window.document.implementation) {
|
||||
doc = window.document.implementation.createHTMLDocument("inert");
|
||||
} else {
|
||||
throw $sanitizeMinErr('noinert', "Can't create an inert html document");
|
||||
}
|
||||
var docElement = doc.documentElement || doc.getDocumentElement();
|
||||
var bodyElements = docElement.getElementsByTagName('body');
|
||||
|
||||
// usually there should be only one body element in the document, but IE doesn't have any, so we need to create one
|
||||
if (bodyElements.length === 1) {
|
||||
inertBodyElement = bodyElements[0];
|
||||
} else {
|
||||
var html = doc.createElement('html');
|
||||
inertBodyElement = doc.createElement('body');
|
||||
html.appendChild(inertBodyElement);
|
||||
doc.appendChild(html);
|
||||
}
|
||||
})(window);
|
||||
|
||||
/**
|
||||
* @example
|
||||
* htmlParser(htmlString, {
|
||||
* start: function(tag, attrs) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* });
|
||||
*
|
||||
* @param {string} html string
|
||||
* @param {object} handler
|
||||
*/
|
||||
function htmlParserImpl(html, handler) {
|
||||
if (html === null || html === undefined) {
|
||||
html = '';
|
||||
} else if (typeof html !== 'string') {
|
||||
html = '' + html;
|
||||
}
|
||||
inertBodyElement.innerHTML = html;
|
||||
|
||||
//mXSS protection
|
||||
var mXSSAttempts = 5;
|
||||
do {
|
||||
if (mXSSAttempts === 0) {
|
||||
throw $sanitizeMinErr('uinput', "Failed to sanitize html because the input is unstable");
|
||||
}
|
||||
mXSSAttempts--;
|
||||
|
||||
// strip custom-namespaced attributes on IE<=11
|
||||
if (window.document.documentMode) {
|
||||
stripCustomNsAttrs(inertBodyElement);
|
||||
}
|
||||
html = inertBodyElement.innerHTML; //trigger mXSS
|
||||
inertBodyElement.innerHTML = html;
|
||||
} while (html !== inertBodyElement.innerHTML);
|
||||
|
||||
var node = inertBodyElement.firstChild;
|
||||
while (node) {
|
||||
switch (node.nodeType) {
|
||||
case 1: // ELEMENT_NODE
|
||||
handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
|
||||
break;
|
||||
case 3: // TEXT NODE
|
||||
handler.chars(node.textContent);
|
||||
break;
|
||||
}
|
||||
|
||||
var nextNode;
|
||||
if (!(nextNode = node.firstChild)) {
|
||||
if (node.nodeType == 1) {
|
||||
handler.end(node.nodeName.toLowerCase());
|
||||
}
|
||||
nextNode = node.nextSibling;
|
||||
if (!nextNode) {
|
||||
while (nextNode == null) {
|
||||
node = node.parentNode;
|
||||
if (node === inertBodyElement) break;
|
||||
nextNode = node.nextSibling;
|
||||
if (node.nodeType == 1) {
|
||||
handler.end(node.nodeName.toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node = nextNode;
|
||||
}
|
||||
|
||||
while (node = inertBodyElement.firstChild) {
|
||||
inertBodyElement.removeChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
function attrToMap(attrs) {
|
||||
var map = {};
|
||||
for (var i = 0, ii = attrs.length; i < ii; i++) {
|
||||
var attr = attrs[i];
|
||||
map[attr.name] = attr.value;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes all potentially dangerous characters, so that the
|
||||
* resulting string can be safely inserted into attribute or
|
||||
* element text.
|
||||
* @param value
|
||||
* @returns {string} escaped text
|
||||
*/
|
||||
function encodeEntities(value) {
|
||||
return value.
|
||||
replace(/&/g, '&').
|
||||
replace(SURROGATE_PAIR_REGEXP, function(value) {
|
||||
var hi = value.charCodeAt(0);
|
||||
var low = value.charCodeAt(1);
|
||||
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
|
||||
}).
|
||||
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
|
||||
return '&#' + value.charCodeAt(0) + ';';
|
||||
}).
|
||||
replace(/</g, '<').
|
||||
replace(/>/g, '>');
|
||||
}
|
||||
|
||||
/**
|
||||
* create an HTML/XML writer which writes to buffer
|
||||
* @param {Array} buf use buf.join('') to get out sanitized html string
|
||||
* @returns {object} in the form of {
|
||||
* start: function(tag, attrs) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* }
|
||||
*/
|
||||
function htmlSanitizeWriterImpl(buf, uriValidator) {
|
||||
var ignoreCurrentElement = false;
|
||||
var out = bind(buf, buf.push);
|
||||
return {
|
||||
start: function(tag, attrs) {
|
||||
tag = lowercase(tag);
|
||||
if (!ignoreCurrentElement && blockedElements[tag]) {
|
||||
ignoreCurrentElement = tag;
|
||||
}
|
||||
if (!ignoreCurrentElement && validElements[tag] === true) {
|
||||
out('<');
|
||||
out(tag);
|
||||
forEach(attrs, function(value, key) {
|
||||
var lkey = lowercase(key);
|
||||
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
|
||||
if (validAttrs[lkey] === true &&
|
||||
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
|
||||
out(' ');
|
||||
out(key);
|
||||
out('="');
|
||||
out(encodeEntities(value));
|
||||
out('"');
|
||||
}
|
||||
});
|
||||
out('>');
|
||||
}
|
||||
},
|
||||
end: function(tag) {
|
||||
tag = lowercase(tag);
|
||||
if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {
|
||||
out('</');
|
||||
out(tag);
|
||||
out('>');
|
||||
}
|
||||
if (tag == ignoreCurrentElement) {
|
||||
ignoreCurrentElement = false;
|
||||
}
|
||||
},
|
||||
chars: function(chars) {
|
||||
if (!ignoreCurrentElement) {
|
||||
out(encodeEntities(chars));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare
|
||||
* ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want
|
||||
* to allow any of these custom attributes. This method strips them all.
|
||||
*
|
||||
* @param node Root element to process
|
||||
*/
|
||||
function stripCustomNsAttrs(node) {
|
||||
if (node.nodeType === window.Node.ELEMENT_NODE) {
|
||||
var attrs = node.attributes;
|
||||
for (var i = 0, l = attrs.length; i < l; i++) {
|
||||
var attrNode = attrs[i];
|
||||
var attrName = attrNode.name.toLowerCase();
|
||||
if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {
|
||||
node.removeAttributeNode(attrNode);
|
||||
i--;
|
||||
l--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var nextNode = node.firstChild;
|
||||
if (nextNode) {
|
||||
stripCustomNsAttrs(nextNode);
|
||||
}
|
||||
|
||||
nextNode = node.nextSibling;
|
||||
if (nextNode) {
|
||||
stripCustomNsAttrs(nextNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sanitizeText(chars) {
|
||||
var buf = [];
|
||||
var writer = htmlSanitizeWriter(buf, angular.noop);
|
||||
var writer = htmlSanitizeWriter(buf, noop);
|
||||
writer.chars(chars);
|
||||
return buf.join('');
|
||||
}
|
||||
|
||||
|
||||
// Regular Expressions for parsing tags and attributes
|
||||
var SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
|
||||
// Match everything outside of normal chars and " (quote character)
|
||||
NON_ALPHANUMERIC_REGEXP = /([^\#-~ |!])/g;
|
||||
|
||||
|
||||
// Good source of info about elements and attributes
|
||||
// http://dev.w3.org/html5/spec/Overview.html#semantics
|
||||
// http://simon.html5.org/html-elements
|
||||
|
||||
// Safe Void Elements - HTML5
|
||||
// http://dev.w3.org/html5/spec/Overview.html#void-elements
|
||||
var voidElements = toMap("area,br,col,hr,img,wbr");
|
||||
|
||||
// Elements that you can, intentionally, leave open (and which close themselves)
|
||||
// http://dev.w3.org/html5/spec/Overview.html#optional-tags
|
||||
var optionalEndTagBlockElements = toMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
|
||||
optionalEndTagInlineElements = toMap("rp,rt"),
|
||||
optionalEndTagElements = angular.extend({},
|
||||
optionalEndTagInlineElements,
|
||||
optionalEndTagBlockElements);
|
||||
|
||||
// Safe Block Elements - HTML5
|
||||
var blockElements = angular.extend({}, optionalEndTagBlockElements, toMap("address,article," +
|
||||
"aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
|
||||
"h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,section,table,ul"));
|
||||
|
||||
// Inline Elements - HTML5
|
||||
var inlineElements = angular.extend({}, optionalEndTagInlineElements, toMap("a,abbr,acronym,b," +
|
||||
"bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
|
||||
"samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
|
||||
|
||||
// SVG Elements
|
||||
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
|
||||
// Note: the elements animate,animateColor,animateMotion,animateTransform,set are intentionally omitted.
|
||||
// They can potentially allow for arbitrary javascript to be executed. See #11290
|
||||
var svgElements = toMap("circle,defs,desc,ellipse,font-face,font-face-name,font-face-src,g,glyph," +
|
||||
"hkern,image,linearGradient,line,marker,metadata,missing-glyph,mpath,path,polygon,polyline," +
|
||||
"radialGradient,rect,stop,svg,switch,text,title,tspan");
|
||||
|
||||
// Blocked Elements (will be stripped)
|
||||
var blockedElements = toMap("script,style");
|
||||
|
||||
var validElements = angular.extend({},
|
||||
voidElements,
|
||||
blockElements,
|
||||
inlineElements,
|
||||
optionalEndTagElements);
|
||||
|
||||
//Attributes that have href and hence need to be sanitized
|
||||
var uriAttrs = toMap("background,cite,href,longdesc,src,xlink:href");
|
||||
|
||||
var htmlAttrs = toMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
|
||||
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
|
||||
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
|
||||
'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
|
||||
'valign,value,vspace,width');
|
||||
|
||||
// SVG attributes (without "id" and "name" attributes)
|
||||
// https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
|
||||
var svgAttrs = toMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
|
||||
'baseProfile,bbox,begin,by,calcMode,cap-height,class,color,color-rendering,content,' +
|
||||
'cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,font-size,font-stretch,' +
|
||||
'font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,gradientUnits,hanging,' +
|
||||
'height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,keySplines,keyTimes,lang,' +
|
||||
'marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mathematical,' +
|
||||
'max,min,offset,opacity,orient,origin,overline-position,overline-thickness,panose-1,' +
|
||||
'path,pathLength,points,preserveAspectRatio,r,refX,refY,repeatCount,repeatDur,' +
|
||||
'requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,stemv,stop-color,' +
|
||||
'stop-opacity,strikethrough-position,strikethrough-thickness,stroke,stroke-dasharray,' +
|
||||
'stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,' +
|
||||
'stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,underline-position,' +
|
||||
'underline-thickness,unicode,unicode-range,units-per-em,values,version,viewBox,visibility,' +
|
||||
'width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,xlink:show,xlink:title,' +
|
||||
'xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,zoomAndPan', true);
|
||||
|
||||
var validAttrs = angular.extend({},
|
||||
uriAttrs,
|
||||
svgAttrs,
|
||||
htmlAttrs);
|
||||
|
||||
function toMap(str, lowercaseKeys) {
|
||||
var obj = {}, items = str.split(','), i;
|
||||
for (i = 0; i < items.length; i++) {
|
||||
obj[lowercaseKeys ? angular.lowercase(items[i]) : items[i]] = true;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
var inertBodyElement;
|
||||
(function(window) {
|
||||
var doc;
|
||||
if (window.document && window.document.implementation) {
|
||||
doc = window.document.implementation.createHTMLDocument("inert");
|
||||
} else {
|
||||
throw $sanitizeMinErr('noinert', "Can't create an inert html document");
|
||||
}
|
||||
var docElement = doc.documentElement || doc.getDocumentElement();
|
||||
var bodyElements = docElement.getElementsByTagName('body');
|
||||
|
||||
// usually there should be only one body element in the document, but IE doesn't have any, so we need to create one
|
||||
if (bodyElements.length === 1) {
|
||||
inertBodyElement = bodyElements[0];
|
||||
} else {
|
||||
var html = doc.createElement('html');
|
||||
inertBodyElement = doc.createElement('body');
|
||||
html.appendChild(inertBodyElement);
|
||||
doc.appendChild(html);
|
||||
}
|
||||
})(window);
|
||||
|
||||
/**
|
||||
* @example
|
||||
* htmlParser(htmlString, {
|
||||
* start: function(tag, attrs) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* });
|
||||
*
|
||||
* @param {string} html string
|
||||
* @param {object} handler
|
||||
*/
|
||||
function htmlParser(html, handler) {
|
||||
if (html === null || html === undefined) {
|
||||
html = '';
|
||||
} else if (typeof html !== 'string') {
|
||||
html = '' + html;
|
||||
}
|
||||
inertBodyElement.innerHTML = html;
|
||||
|
||||
//mXSS protection
|
||||
var mXSSAttempts = 5;
|
||||
do {
|
||||
if (mXSSAttempts === 0) {
|
||||
throw $sanitizeMinErr('uinput', "Failed to sanitize html because the input is unstable");
|
||||
}
|
||||
mXSSAttempts--;
|
||||
|
||||
// strip custom-namespaced attributes on IE<=11
|
||||
if (window.document.documentMode) {
|
||||
stripCustomNsAttrs(inertBodyElement);
|
||||
}
|
||||
html = inertBodyElement.innerHTML; //trigger mXSS
|
||||
inertBodyElement.innerHTML = html;
|
||||
} while (html !== inertBodyElement.innerHTML);
|
||||
|
||||
var node = inertBodyElement.firstChild;
|
||||
while (node) {
|
||||
switch (node.nodeType) {
|
||||
case 1: // ELEMENT_NODE
|
||||
handler.start(node.nodeName.toLowerCase(), attrToMap(node.attributes));
|
||||
break;
|
||||
case 3: // TEXT NODE
|
||||
handler.chars(node.textContent);
|
||||
break;
|
||||
}
|
||||
|
||||
var nextNode;
|
||||
if (!(nextNode = node.firstChild)) {
|
||||
if (node.nodeType == 1) {
|
||||
handler.end(node.nodeName.toLowerCase());
|
||||
}
|
||||
nextNode = node.nextSibling;
|
||||
if (!nextNode) {
|
||||
while (nextNode == null) {
|
||||
node = node.parentNode;
|
||||
if (node === inertBodyElement) break;
|
||||
nextNode = node.nextSibling;
|
||||
if (node.nodeType == 1) {
|
||||
handler.end(node.nodeName.toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node = nextNode;
|
||||
}
|
||||
|
||||
while (node = inertBodyElement.firstChild) {
|
||||
inertBodyElement.removeChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
function attrToMap(attrs) {
|
||||
var map = {};
|
||||
for (var i = 0, ii = attrs.length; i < ii; i++) {
|
||||
var attr = attrs[i];
|
||||
map[attr.name] = attr.value;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Escapes all potentially dangerous characters, so that the
|
||||
* resulting string can be safely inserted into attribute or
|
||||
* element text.
|
||||
* @param value
|
||||
* @returns {string} escaped text
|
||||
*/
|
||||
function encodeEntities(value) {
|
||||
return value.
|
||||
replace(/&/g, '&').
|
||||
replace(SURROGATE_PAIR_REGEXP, function(value) {
|
||||
var hi = value.charCodeAt(0);
|
||||
var low = value.charCodeAt(1);
|
||||
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
|
||||
}).
|
||||
replace(NON_ALPHANUMERIC_REGEXP, function(value) {
|
||||
return '&#' + value.charCodeAt(0) + ';';
|
||||
}).
|
||||
replace(/</g, '<').
|
||||
replace(/>/g, '>');
|
||||
}
|
||||
|
||||
/**
|
||||
* create an HTML/XML writer which writes to buffer
|
||||
* @param {Array} buf use buf.join('') to get out sanitized html string
|
||||
* @returns {object} in the form of {
|
||||
* start: function(tag, attrs) {},
|
||||
* end: function(tag) {},
|
||||
* chars: function(text) {},
|
||||
* comment: function(text) {}
|
||||
* }
|
||||
*/
|
||||
function htmlSanitizeWriter(buf, uriValidator) {
|
||||
var ignoreCurrentElement = false;
|
||||
var out = angular.bind(buf, buf.push);
|
||||
return {
|
||||
start: function(tag, attrs) {
|
||||
tag = angular.lowercase(tag);
|
||||
if (!ignoreCurrentElement && blockedElements[tag]) {
|
||||
ignoreCurrentElement = tag;
|
||||
}
|
||||
if (!ignoreCurrentElement && validElements[tag] === true) {
|
||||
out('<');
|
||||
out(tag);
|
||||
angular.forEach(attrs, function(value, key) {
|
||||
var lkey=angular.lowercase(key);
|
||||
var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
|
||||
if (validAttrs[lkey] === true &&
|
||||
(uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
|
||||
out(' ');
|
||||
out(key);
|
||||
out('="');
|
||||
out(encodeEntities(value));
|
||||
out('"');
|
||||
}
|
||||
});
|
||||
out('>');
|
||||
}
|
||||
},
|
||||
end: function(tag) {
|
||||
tag = angular.lowercase(tag);
|
||||
if (!ignoreCurrentElement && validElements[tag] === true && voidElements[tag] !== true) {
|
||||
out('</');
|
||||
out(tag);
|
||||
out('>');
|
||||
}
|
||||
if (tag == ignoreCurrentElement) {
|
||||
ignoreCurrentElement = false;
|
||||
}
|
||||
},
|
||||
chars: function(chars) {
|
||||
if (!ignoreCurrentElement) {
|
||||
out(encodeEntities(chars));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When IE9-11 comes across an unknown namespaced attribute e.g. 'xlink:foo' it adds 'xmlns:ns1' attribute to declare
|
||||
* ns1 namespace and prefixes the attribute with 'ns1' (e.g. 'ns1:xlink:foo'). This is undesirable since we don't want
|
||||
* to allow any of these custom attributes. This method strips them all.
|
||||
*
|
||||
* @param node Root element to process
|
||||
*/
|
||||
function stripCustomNsAttrs(node) {
|
||||
if (node.nodeType === window.Node.ELEMENT_NODE) {
|
||||
var attrs = node.attributes;
|
||||
for (var i = 0, l = attrs.length; i < l; i++) {
|
||||
var attrNode = attrs[i];
|
||||
var attrName = attrNode.name.toLowerCase();
|
||||
if (attrName === 'xmlns:ns1' || attrName.lastIndexOf('ns1:', 0) === 0) {
|
||||
node.removeAttributeNode(attrNode);
|
||||
i--;
|
||||
l--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var nextNode = node.firstChild;
|
||||
if (nextNode) {
|
||||
stripCustomNsAttrs(nextNode);
|
||||
}
|
||||
|
||||
nextNode = node.nextSibling;
|
||||
if (nextNode) {
|
||||
stripCustomNsAttrs(nextNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// define ngSanitize module and register $sanitize service
|
||||
angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
|
||||
|
||||
@@ -80,6 +80,17 @@
|
||||
}
|
||||
} else if (/touch/.test(eventType) && supportsTouchEvents()) {
|
||||
evnt = createTouchEvent(element, eventType, x, y);
|
||||
} else if (/key/.test(eventType)) {
|
||||
evnt = window.document.createEvent('Events');
|
||||
evnt.initEvent(eventType, eventData.bubbles, eventData.cancelable);
|
||||
evnt.view = window;
|
||||
evnt.ctrlKey = pressed('ctrl');
|
||||
evnt.altKey = pressed('alt');
|
||||
evnt.shiftKey = pressed('shift');
|
||||
evnt.metaKey = pressed('meta');
|
||||
evnt.keyCode = eventData.keyCode;
|
||||
evnt.charCode = eventData.charCode;
|
||||
evnt.which = eventData.which;
|
||||
} else {
|
||||
evnt = window.document.createEvent('MouseEvents');
|
||||
x = x || 0;
|
||||
@@ -108,7 +119,12 @@
|
||||
return originalPreventDefault.apply(evnt, arguments);
|
||||
};
|
||||
|
||||
element.dispatchEvent(evnt);
|
||||
if (!eventData.bubbles || supportsEventBubblingInDetachedTree() || isAttachedToDocument(element)) {
|
||||
element.dispatchEvent(evnt);
|
||||
} else {
|
||||
triggerForPath(element, evnt);
|
||||
}
|
||||
|
||||
finalProcessDefault = !(angular['ff-684208-preventDefault'] || !fakeProcessDefault);
|
||||
|
||||
delete angular['ff-684208-preventDefault'];
|
||||
@@ -146,4 +162,52 @@
|
||||
|
||||
return evnt;
|
||||
}
|
||||
|
||||
function supportsEventBubblingInDetachedTree() {
|
||||
if ('_cached' in supportsEventBubblingInDetachedTree) {
|
||||
return supportsEventBubblingInDetachedTree._cached;
|
||||
}
|
||||
supportsEventBubblingInDetachedTree._cached = false;
|
||||
var doc = window.document;
|
||||
if (doc) {
|
||||
var parent = doc.createElement('div'),
|
||||
child = parent.cloneNode();
|
||||
parent.appendChild(child);
|
||||
parent.addEventListener('e', function() {
|
||||
supportsEventBubblingInDetachedTree._cached = true;
|
||||
});
|
||||
var evnt = window.document.createEvent('Events');
|
||||
evnt.initEvent('e', true, true);
|
||||
child.dispatchEvent(evnt);
|
||||
}
|
||||
return supportsEventBubblingInDetachedTree._cached;
|
||||
}
|
||||
|
||||
function triggerForPath(element, evnt) {
|
||||
var stop = false;
|
||||
|
||||
var _stopPropagation = evnt.stopPropagation;
|
||||
evnt.stopPropagation = function() {
|
||||
stop = true;
|
||||
_stopPropagation.apply(evnt, arguments);
|
||||
};
|
||||
patchEventTargetForBubbling(evnt, element);
|
||||
do {
|
||||
element.dispatchEvent(evnt);
|
||||
} while (!stop && (element = element.parentNode));
|
||||
}
|
||||
|
||||
function patchEventTargetForBubbling(event, target) {
|
||||
event._target = target;
|
||||
Object.defineProperty(event, "target", {get: function() { return this._target;}});
|
||||
}
|
||||
|
||||
function isAttachedToDocument(element) {
|
||||
while (element = element.parentNode) {
|
||||
if (element === window) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}());
|
||||
|
||||
@@ -35,6 +35,12 @@ ngTouch.factory('$swipe', [function() {
|
||||
move: 'touchmove',
|
||||
end: 'touchend',
|
||||
cancel: 'touchcancel'
|
||||
},
|
||||
'pointer': {
|
||||
start: 'pointerdown',
|
||||
move: 'pointermove',
|
||||
end: 'pointerup',
|
||||
cancel: 'pointercancel'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -69,15 +75,15 @@ ngTouch.factory('$swipe', [function() {
|
||||
* The main method of `$swipe`. It takes an element to be watched for swipe motions, and an
|
||||
* object containing event handlers.
|
||||
* The pointer types that should be used can be specified via the optional
|
||||
* third argument, which is an array of strings `'mouse'` and `'touch'`. By default,
|
||||
* `$swipe` will listen for `mouse` and `touch` events.
|
||||
* third argument, which is an array of strings `'mouse'`, `'touch'` and `'pointer'`. By default,
|
||||
* `$swipe` will listen for `mouse`, `touch` and `pointer` events.
|
||||
*
|
||||
* The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end`
|
||||
* receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }` and the raw
|
||||
* `event`. `cancel` receives the raw `event` as its single parameter.
|
||||
*
|
||||
* `start` is called on either `mousedown` or `touchstart`. After this event, `$swipe` is
|
||||
* watching for `touchmove` or `mousemove` events. These events are ignored until the total
|
||||
* `start` is called on either `mousedown`, `touchstart` or `pointerdown`. After this event, `$swipe` is
|
||||
* watching for `touchmove`, `mousemove` or `pointermove` events. These events are ignored until the total
|
||||
* distance moved in either dimension exceeds a small threshold.
|
||||
*
|
||||
* Once this threshold is exceeded, either the horizontal or vertical delta is greater.
|
||||
@@ -85,12 +91,12 @@ ngTouch.factory('$swipe', [function() {
|
||||
* - If the vertical distance is greater, this is a scroll, and we let the browser take over.
|
||||
* A `cancel` event is sent.
|
||||
*
|
||||
* `move` is called on `mousemove` and `touchmove` after the above logic has determined that
|
||||
* `move` is called on `mousemove`, `touchmove` and `pointermove` after the above logic has determined that
|
||||
* a swipe is in progress.
|
||||
*
|
||||
* `end` is called when a swipe is successfully completed with a `touchend` or `mouseup`.
|
||||
* `end` is called when a swipe is successfully completed with a `touchend`, `mouseup` or `pointerup`.
|
||||
*
|
||||
* `cancel` is called either on a `touchcancel` from the browser, or when we begin scrolling
|
||||
* `cancel` is called either on a `touchcancel` or `pointercancel` from the browser, or when we begin scrolling
|
||||
* as described above.
|
||||
*
|
||||
*/
|
||||
@@ -104,7 +110,7 @@ ngTouch.factory('$swipe', [function() {
|
||||
// Whether a swipe is active.
|
||||
var active = false;
|
||||
|
||||
pointerTypes = pointerTypes || ['mouse', 'touch'];
|
||||
pointerTypes = pointerTypes || ['mouse', 'touch', 'pointer'];
|
||||
element.on(getEvents(pointerTypes, 'start'), function(event) {
|
||||
startCoords = getCoordinates(event);
|
||||
active = true;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
'use strict';
|
||||
|
||||
/* global shallowCopy: true */
|
||||
|
||||
/**
|
||||
* Creates a shallow copy of an object, an array or a primitive.
|
||||
*
|
||||
* Assumes that there are no proto properties for objects.
|
||||
*/
|
||||
function shallowCopy(src, dst) {
|
||||
if (isArray(src)) {
|
||||
dst = dst || [];
|
||||
|
||||
for (var i = 0, ii = src.length; i < ii; i++) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
} else if (isObject(src)) {
|
||||
dst = dst || {};
|
||||
|
||||
for (var key in src) {
|
||||
if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
|
||||
dst[key] = src[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dst || src;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
/* global: toDebugString: true */
|
||||
/* global toDebugString: true */
|
||||
|
||||
function serializeObject(obj) {
|
||||
var seen = [];
|
||||
|
||||
@@ -240,6 +240,20 @@ describe('angular', function() {
|
||||
}
|
||||
});
|
||||
|
||||
it("should handle Uint16Array subarray", function() {
|
||||
if (typeof Uint16Array !== 'undefined') {
|
||||
var arr = new Uint16Array(4);
|
||||
arr[1] = 1;
|
||||
var src = arr.subarray(1, 2);
|
||||
var dst = copy(src);
|
||||
expect(dst instanceof Uint16Array).toBeTruthy();
|
||||
expect(dst.length).toEqual(1);
|
||||
expect(dst[0]).toEqual(1);
|
||||
expect(dst).not.toBe(src);
|
||||
expect(dst.buffer).not.toBe(src.buffer);
|
||||
}
|
||||
});
|
||||
|
||||
it("should throw an exception if a Uint8Array is the destination", function() {
|
||||
if (typeof Uint8Array !== 'undefined') {
|
||||
var src = new Uint8Array();
|
||||
|
||||
@@ -302,6 +302,26 @@ describe('injector', function() {
|
||||
expect(instance.aVal()).toEqual('a-value');
|
||||
});
|
||||
|
||||
if (/chrome/.test(navigator.userAgent)) {
|
||||
they('should detect ES6 classes regardless of whitespace/comments ($prop)', [
|
||||
'class Test {}',
|
||||
'class Test{}',
|
||||
'class //<--ES6 stuff\nTest {}',
|
||||
'class//<--ES6 stuff\nTest {}',
|
||||
'class {}',
|
||||
'class{}',
|
||||
'class //<--ES6 stuff\n {}',
|
||||
'class//<--ES6 stuff\n {}',
|
||||
'class/* Test */{}',
|
||||
'class /* Test */ {}'
|
||||
], function(classDefinition) {
|
||||
var Clazz = eval('(' + classDefinition + ')');
|
||||
var instance = injector.invoke(Clazz);
|
||||
|
||||
expect(instance).toEqual(jasmine.any(Clazz));
|
||||
});
|
||||
}
|
||||
|
||||
// Support: Chrome 50-51 only
|
||||
// TODO (gkalpak): Remove when Chrome v52 is relased.
|
||||
// it('should be able to invoke classes', function() {
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="test">
|
||||
<body>
|
||||
<div class="fixed-header" ng-controller="TestController">
|
||||
<button ng-click="scrollTo('anchor-' + x)" ng-repeat="x in [1, 2, 3, 4, 5]">
|
||||
Scroll to anchor-{{x}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="anchor" id="anchor-{{y}}" ng-repeat="y in [1, 2, 3, 4, 5]">
|
||||
Anchor {{y}} of 5
|
||||
</div>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding-top: 50px;
|
||||
}
|
||||
.anchor {
|
||||
border: 2px dashed darkorchid;
|
||||
padding: 10px 10px 390px 10px;
|
||||
}
|
||||
.fixed-header {
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
height: 50px;
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0;
|
||||
}
|
||||
.fixed-header > button {
|
||||
display: inline-block;
|
||||
margin: 5px 15px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="angular.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,17 @@
|
||||
angular.
|
||||
module('test', []).
|
||||
controller('TestController', function($anchorScroll, $location, $scope) {
|
||||
$anchorScroll.yOffset = 50;
|
||||
|
||||
$scope.scrollTo = function(target) {
|
||||
if ($location.hash() !== target) {
|
||||
// Set `$location.hash()` to `target` and
|
||||
// `$anchorScroll` will detect the change and scroll
|
||||
$location.hash(target);
|
||||
} else {
|
||||
// The hash is the same, but `target` might be out of view -
|
||||
// explicitly call `$anchorScroll`
|
||||
$anchorScroll();
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="test">
|
||||
<body>
|
||||
<div class="scroll-area" ng-controller="TestController">
|
||||
<a id="top" ng-click="scrollTo('bottom')">Go to bottom</a>
|
||||
<a id="bottom" ng-click="scrollTo('top')">Back to top</a>
|
||||
</div>
|
||||
|
||||
<style type="text/css">
|
||||
.scroll-area {
|
||||
height: 200px;
|
||||
overflow: auto;
|
||||
}
|
||||
#bottom {
|
||||
display: block;
|
||||
margin-top: 2000px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="angular.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,9 @@
|
||||
angular.
|
||||
module('test', []).
|
||||
controller('TestController', function($anchorScroll, $location, $scope) {
|
||||
$scope.scrollTo = function(target) {
|
||||
// Set `$location.hash()` to `target` and
|
||||
// `$anchorScroll` will detect the change and scroll
|
||||
$location.hash(target);
|
||||
};
|
||||
});
|
||||
@@ -1,10 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="test">
|
||||
<div ng-controller="TestCtrl">
|
||||
<p>{{text}}</p>
|
||||
</div>
|
||||
<body>
|
||||
<div ng-controller="TestController">
|
||||
<p>{{text}}</p>
|
||||
</div>
|
||||
|
||||
<script src="angular.js"></script>
|
||||
<script src="angular.js"></script>
|
||||
<script src="script.js"></script>
|
||||
<script src="angular.js"></script>
|
||||
<script src="angular.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
angular.module("test", []).
|
||||
controller("TestCtrl", function($scope) {
|
||||
$scope.text = "Hello, world!";
|
||||
angular.
|
||||
module('test', []).
|
||||
controller('TestController', function($scope) {
|
||||
$scope.text = 'Hello, world!';
|
||||
});
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="test">
|
||||
<body>
|
||||
<div ng-controller="TestController">
|
||||
<p>{{text}}</p>
|
||||
</div>
|
||||
|
||||
<!-- Load modules before `angular.js` -->
|
||||
<script src="angular-loader.js"></script>
|
||||
<script src="angular-touch.js"></script>
|
||||
<script src="angular-sanitize.js"></script>
|
||||
<script src="angular-route.js"></script>
|
||||
<script src="angular-resource.js"></script>
|
||||
<script src="angular-parse-ext.js"></script>
|
||||
<script src="angular-messages.js"></script>
|
||||
<script src="angular-message-format.js"></script>
|
||||
<script src="angular-cookies.js"></script>
|
||||
<script src="angular-aria.js"></script>
|
||||
<script src="angular-animate.js"></script>
|
||||
<script src="angular.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,16 @@
|
||||
angular.
|
||||
module('test', [
|
||||
'ngTouch',
|
||||
'ngSanitize',
|
||||
'ngRoute',
|
||||
'ngResource',
|
||||
'ngParseExt',
|
||||
'ngMessages',
|
||||
'ngMessageFormat',
|
||||
'ngCookies',
|
||||
'ngAria',
|
||||
'ngAnimate'
|
||||
]).
|
||||
controller('TestController', function($scope) {
|
||||
$scope.text = 'Hello, world!';
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html ng-app="test" ng-jq="jQuery_2_1_0">
|
||||
<body>
|
||||
{{jqueryVersion}}
|
||||
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
|
||||
<script>
|
||||
var jQuery_2_1_0 = jQuery.noConflict();
|
||||
</script>
|
||||
<script src="../../../../bower_components/jquery/dist/jquery.js"></script>
|
||||
<script src="angular.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +1,5 @@
|
||||
angular.module('test', [])
|
||||
.run(function($rootScope) {
|
||||
angular.
|
||||
module('test', []).
|
||||
run(function($rootScope) {
|
||||
$rootScope.jqueryVersion = window.angular.element().jquery || 'jqLite';
|
||||
});
|
||||
@@ -10,6 +10,6 @@
|
||||
window[''] = window.jQuery;
|
||||
</script>
|
||||
<script src="angular.js"></script>
|
||||
<script type="text/javascript" src="script.js"></script>
|
||||
<script src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||