Compare commits
86 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 |
@@ -1,3 +1,178 @@
|
||||
<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)
|
||||
|
||||
@@ -512,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
|
||||
@@ -1443,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)
|
||||
|
||||
|
||||
@@ -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']);
|
||||
|
||||
@@ -28,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',
|
||||
@@ -236,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',
|
||||
@@ -253,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}.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -100,7 +100,8 @@ The set of files included in each version directory are:
|
||||
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`__ — ({@link module:ngMessageFormat 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.
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
|
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',
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var isOnlineAsync = require('is-online');
|
||||
|
||||
isOnlineAsync(function(err, isOnline) {
|
||||
console.log(isOnline ? 'online' : 'offline');
|
||||
});
|
||||
@@ -1,16 +1,18 @@
|
||||
'use strict';
|
||||
|
||||
var childProcess = require('child_process');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
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;
|
||||
var connectivityStatus = childProcess.execSync('node is-online.js', { cwd: __dirname, encoding: 'utf8' }).trim();
|
||||
var isOnline = connectivityStatus == 'online';
|
||||
console.log('Running ' + connectivityStatus);
|
||||
|
||||
|
||||
/**
|
||||
* Load information about this project from the package.json
|
||||
@@ -99,72 +101,68 @@ 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;
|
||||
if (isOnline) {
|
||||
console.log('looking up remote git tags...');
|
||||
var tagResults = shell.exec('git ls-remote --tags ' + repo_url,
|
||||
{silent: true});
|
||||
if (tagResults.code === 0) {
|
||||
return _(tagResults.output.match(/v[0-9].*[0-9]$/mg))
|
||||
.map(function(tag) {
|
||||
var version = semver.parse(tag);
|
||||
return version;
|
||||
})
|
||||
.filter()
|
||||
.map(function(version) {
|
||||
// angular.js didn't follow semantic version until 1.20rc1
|
||||
if ((version.major === 1 && version.minor === 0 && version.prerelease.length > 0) || (version.major === 1 && version.minor === 2 && version.prerelease[0] === 'rc1')) {
|
||||
version.version = [version.major, version.minor, version.patch].join('.') + version.prerelease.join('');
|
||||
version.raw = 'v' + version.version;
|
||||
}
|
||||
version.docsUrl = 'http://code.angularjs.org/' + version.version + '/docs';
|
||||
// Versions before 1.0.2 had a different docs folder name
|
||||
if (version.major < 1 || (version.major === 1 && version.minor === 0 && version.patch < 2)) {
|
||||
version.docsUrl += '-' + version.version;
|
||||
version.isOldDocsUrl = true;
|
||||
}
|
||||
return version;
|
||||
})
|
||||
.sort(semver.compare)
|
||||
.value();
|
||||
}
|
||||
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) {
|
||||
var version = semver.parse(tag);
|
||||
return version;
|
||||
})
|
||||
.filter()
|
||||
.map(function(version) {
|
||||
// angular.js didn't follow semantic version until 1.20rc1
|
||||
if ((version.major === 1 && version.minor === 0 && version.prerelease.length > 0) || (version.major === 1 && version.minor === 2 && version.prerelease[0] === 'rc1')) {
|
||||
version.version = [version.major, version.minor, version.patch].join('.') + version.prerelease.join('');
|
||||
version.raw = 'v' + version.version;
|
||||
}
|
||||
version.docsUrl = 'http://code.angularjs.org/' + version.version + '/docs';
|
||||
// Versions before 1.0.2 had a different docs folder name
|
||||
if (version.major < 1 || (version.major === 1 && version.minor === 0 && version.patch < 2)) {
|
||||
version.docsUrl += '-' + version.version;
|
||||
version.isOldDocsUrl = true;
|
||||
}
|
||||
return version;
|
||||
})
|
||||
.sort(semver.compare)
|
||||
.value();
|
||||
} else {
|
||||
console.log('-- skipping remote git tag lookup');
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
var getCdnVersion = function() {
|
||||
if (isOnline) {
|
||||
console.log('searching for CDN versions...');
|
||||
return _(previousVersions)
|
||||
.filter(function(tag) {
|
||||
return semver.satisfies(tag, currentPackage.branchVersion);
|
||||
})
|
||||
.reverse()
|
||||
.reduce(function(cdnVersion, version) {
|
||||
if (!cdnVersion) {
|
||||
return _(previousVersions)
|
||||
.filter(function(tag) {
|
||||
return semver.satisfies(tag, currentPackage.branchVersion);
|
||||
})
|
||||
.reverse()
|
||||
.reduce(function(cdnVersion, version) {
|
||||
if (!cdnVersion) {
|
||||
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}" -o /dev/null -silent',
|
||||
'--head --write-out "%{http_code}" -silent',
|
||||
{silent: true});
|
||||
if (cdnResult.code === 0) {
|
||||
var statusCode = cdnResult.output.trim();
|
||||
// --write-out appends its content to the general request response, so extract it
|
||||
var statusCode = cdnResult.output.split('\n').pop().trim();
|
||||
if (statusCode === '200') {
|
||||
console.log('-- found CDN version ' + version);
|
||||
cdnVersion = version;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cdnVersion;
|
||||
}, null);
|
||||
} else {
|
||||
console.log('-- skipping CDN version lookup');
|
||||
}
|
||||
}
|
||||
return cdnVersion;
|
||||
}, null);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -211,8 +209,20 @@ var getSnapshotVersion = function() {
|
||||
return version;
|
||||
};
|
||||
|
||||
|
||||
exports.currentPackage = currentPackage = getPackage();
|
||||
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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6392,50 +6396,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"is-online": {
|
||||
"version": "5.1.1",
|
||||
"dependencies": {
|
||||
"is-reachable": {
|
||||
"version": "1.3.0",
|
||||
"dependencies": {
|
||||
"arrify": {
|
||||
"version": "1.0.1"
|
||||
},
|
||||
"each-async": {
|
||||
"version": "1.1.1",
|
||||
"dependencies": {
|
||||
"set-immediate-shim": {
|
||||
"version": "1.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"is-port-reachable": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"router-ips": {
|
||||
"version": "0.2.0"
|
||||
},
|
||||
"url-parse-lax": {
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"prepend-http": {
|
||||
"version": "1.0.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"onetime": {
|
||||
"version": "1.1.0"
|
||||
},
|
||||
"random-item": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"root-hints": {
|
||||
"version": "1.0.10"
|
||||
}
|
||||
}
|
||||
},
|
||||
"jasmine-core": {
|
||||
"version": "2.4.1"
|
||||
},
|
||||
@@ -6503,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"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -6963,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-regex": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1"
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.2"
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3"
|
||||
},
|
||||
"assert-plus": {
|
||||
"version": "0.2.0"
|
||||
},
|
||||
"async": {
|
||||
"version": "1.5.2"
|
||||
},
|
||||
"bl": {
|
||||
"version": "1.0.3"
|
||||
},
|
||||
"aws-sign2": {
|
||||
"version": "0.6.0"
|
||||
},
|
||||
"block-stream": {
|
||||
"version": "0.0.8"
|
||||
},
|
||||
"boom": {
|
||||
"version": "2.10.1"
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.11.0"
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3"
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.5"
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.9.0"
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.4.1"
|
||||
},
|
||||
"cryptiles": {
|
||||
"version": "2.0.5"
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.1"
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.2.0"
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.0"
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5"
|
||||
},
|
||||
"extsprintf": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1"
|
||||
},
|
||||
"gauge": {
|
||||
"version": "1.2.7"
|
||||
},
|
||||
"form-data": {
|
||||
"version": "1.0.0-rc4"
|
||||
},
|
||||
"fstream": {
|
||||
"version": "1.0.8"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"hawk": {
|
||||
"version": "3.1.3"
|
||||
},
|
||||
"hoek": {
|
||||
"version": "2.16.3"
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.4"
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.1"
|
||||
},
|
||||
"is-my-json-valid": {
|
||||
"version": "2.13.1"
|
||||
},
|
||||
"is-property": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2"
|
||||
},
|
||||
"jodid25519": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.2"
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.0"
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1"
|
||||
},
|
||||
"jsonpointer": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.2.2"
|
||||
},
|
||||
"lodash.pad": {
|
||||
"version": "4.1.0"
|
||||
},
|
||||
"lodash.padend": {
|
||||
"version": "4.2.0"
|
||||
},
|
||||
"lodash.padstart": {
|
||||
"version": "4.2.0"
|
||||
},
|
||||
"lodash.repeat": {
|
||||
"version": "4.0.0"
|
||||
},
|
||||
"lodash.tostring": {
|
||||
"version": "4.1.2"
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.10"
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.22.0"
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8"
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1"
|
||||
},
|
||||
"ms": {
|
||||
"version": "0.7.1"
|
||||
},
|
||||
"node-uuid": {
|
||||
"version": "1.4.7"
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "2.0.3"
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.8.1"
|
||||
},
|
||||
"once": {
|
||||
"version": "1.3.3"
|
||||
},
|
||||
"pinkie": {
|
||||
"version": "2.0.4"
|
||||
},
|
||||
"pinkie-promise": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.0.2"
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "1.0.6"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31"
|
||||
},
|
||||
"stringstream": {
|
||||
"version": "0.0.5"
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "1.0.4"
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1"
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0"
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.2.2"
|
||||
},
|
||||
"tar-pack": {
|
||||
"version": "3.1.3"
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.4.2"
|
||||
},
|
||||
"tar": {
|
||||
"version": "2.2.1"
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.14.3"
|
||||
},
|
||||
"uid-number": {
|
||||
"version": "0.0.6"
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2"
|
||||
},
|
||||
"verror": {
|
||||
"version": "1.3.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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -8832,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",
|
||||
@@ -52,10 +52,9 @@
|
||||
"gulp-sourcemaps": "^1.2.2",
|
||||
"gulp-uglify": "^1.0.1",
|
||||
"gulp-util": "^3.0.1",
|
||||
"is-online": "^5.1.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 */
|
||||
|
||||
@@ -752,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`.
|
||||
@@ -761,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 = [];
|
||||
@@ -902,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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
@@ -1964,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;
|
||||
}
|
||||
|
||||
@@ -1984,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.
|
||||
@@ -2512,6 +2619,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
}
|
||||
if (isFunction(controllerInstance.$doCheck)) {
|
||||
controllerScope.$watch(function() { controllerInstance.$doCheck(); });
|
||||
controllerInstance.$doCheck();
|
||||
}
|
||||
if (isFunction(controllerInstance.$onDestroy)) {
|
||||
controllerScope.$on('$destroy', function callOnDestroyHook() {
|
||||
controllerInstance.$onDestroy();
|
||||
@@ -3158,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">
|
||||
|
||||
@@ -1411,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
|
||||
@@ -1427,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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
}];
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
* logErrorsToBackend(exception, cause);
|
||||
* $log.warn(exception, cause);
|
||||
* };
|
||||
* });
|
||||
* }]);
|
||||
* ```
|
||||
*
|
||||
* <hr />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
};
|
||||
}];
|
||||
};
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,7 +259,14 @@ 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
|
||||
|
||||
@@ -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
|
||||
@@ -2274,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"`
|
||||
*
|
||||
@@ -2923,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);
|
||||
@@ -3026,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);
|
||||
|
||||
@@ -114,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 `?`.
|
||||
@@ -123,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
|
||||
@@ -143,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,
|
||||
@@ -641,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;
|
||||
});
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
/* global shallowCopy: false */
|
||||
|
||||
// There are necessary for `shallowCopy()` (included via `src/shallowCopy.js`)
|
||||
var isArray = angular.isArray;
|
||||
var isObject = angular.isObject;
|
||||
// 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
|
||||
@@ -41,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);
|
||||
}
|
||||
|
||||
@@ -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,6 +133,9 @@ 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) {
|
||||
@@ -142,8 +143,8 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
if (!isString(text)) throw linkyMinErr('notstring', 'Expected string but received: {0}', text);
|
||||
|
||||
var attributesFn =
|
||||
angular.isFunction(attributes) ? attributes :
|
||||
angular.isObject(attributes) ? function getAttributesObject() {return attributes;} :
|
||||
isFunction(attributes) ? attributes :
|
||||
isObject(attributes) ? function getAttributesObject() {return attributes;} :
|
||||
function getEmptyAttributesObject() {return {};};
|
||||
|
||||
var match;
|
||||
@@ -181,7 +182,7 @@ angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
html.push(key + '="' + linkAttributes[key] + '" ');
|
||||
}
|
||||
|
||||
if (angular.isDefined(target) && !('target' in linkAttributes)) {
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
@@ -1,4 +1,5 @@
|
||||
angular.module('test', [])
|
||||
.run(function($rootScope) {
|
||||
angular.
|
||||
module('test', []).
|
||||
run(function($rootScope) {
|
||||
$rootScope.jqueryVersion = window.angular.element().jquery || 'jqLite';
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
<!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 type="text/javascript" src="script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,9 +1,11 @@
|
||||
<!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="script.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,187 @@
|
||||
describe('$anchorScroll', function() {
|
||||
beforeEach(function() {
|
||||
jasmine.addMatchers({
|
||||
toBeInViewport: function() {
|
||||
return {
|
||||
compare: function(id) {
|
||||
var result = {
|
||||
pass: browser.driver.
|
||||
executeScript(_script_isInViewport, id).
|
||||
then(function(isInViewport) {
|
||||
result.message = 'Expected #' + id + (isInViewport ? ' not' : '') +
|
||||
' to be in viewport';
|
||||
return isInViewport;
|
||||
})
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
},
|
||||
toHaveTop: function() {
|
||||
return {
|
||||
compare: function(id, expectedTop) {
|
||||
var result = {
|
||||
pass: browser.driver.
|
||||
executeScript(_script_getTop, id).
|
||||
then(function(actualTop) {
|
||||
var passed = actualTop === expectedTop;
|
||||
result.message = 'Expected #' + id + '\'s top' + (passed ? ' not' : '') +
|
||||
' to be ' + expectedTop + ', but it was ' + actualTop;
|
||||
return passed;
|
||||
})
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('basic functionality', function() {
|
||||
beforeEach(function() {
|
||||
loadFixture('anchor-scroll');
|
||||
});
|
||||
|
||||
it('should scroll to #bottom when clicking #top and vice versa', function() {
|
||||
expect('top').toBeInViewport();
|
||||
expect('bottom').not.toBeInViewport();
|
||||
|
||||
element(by.id('top')).click();
|
||||
expect('top').not.toBeInViewport();
|
||||
expect('bottom').toBeInViewport();
|
||||
|
||||
element(by.id('bottom')).click();
|
||||
expect('top').toBeInViewport();
|
||||
expect('bottom').not.toBeInViewport();
|
||||
});
|
||||
});
|
||||
|
||||
describe('with `yOffset`', function() {
|
||||
var yOffset = 50;
|
||||
var buttons = element.all(by.repeater('x in [1, 2, 3, 4, 5]'));
|
||||
var anchors = element.all(by.repeater('y in [1, 2, 3, 4, 5]'));
|
||||
|
||||
beforeEach(function() {
|
||||
loadFixture('anchor-scroll-y-offset');
|
||||
});
|
||||
|
||||
it('should scroll to the correct anchor when clicking each button', function() {
|
||||
var lastAnchor = anchors.last();
|
||||
|
||||
// Make sure there is enough room to scroll the last anchor to the top
|
||||
lastAnchor.getSize().then(function(size) {
|
||||
var tempHeight = size.height - 10;
|
||||
|
||||
execWithTempViewportHeight(tempHeight, function() {
|
||||
buttons.each(function(button, idx) {
|
||||
// For whatever reason, we need to run the assertions inside a callback :(
|
||||
button.click().then(function() {
|
||||
var anchorId = 'anchor-' + (idx + 1);
|
||||
|
||||
expect(anchorId).toBeInViewport();
|
||||
expect(anchorId).toHaveTop(yOffset);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should automatically scroll when navigating to a URL with a hash', function() {
|
||||
var lastAnchor = anchors.last();
|
||||
var lastAnchorId = 'anchor-5';
|
||||
|
||||
// Make sure there is enough room to scroll the last anchor to the top
|
||||
lastAnchor.getSize().then(function(size) {
|
||||
var tempHeight = size.height - 10;
|
||||
|
||||
execWithTempViewportHeight(tempHeight, function() {
|
||||
// Test updating `$location.url()` from within the app
|
||||
expect(lastAnchorId).not.toBeInViewport();
|
||||
|
||||
browser.setLocation('#' + lastAnchorId);
|
||||
expect(lastAnchorId).toBeInViewport();
|
||||
expect(lastAnchorId).toHaveTop(yOffset);
|
||||
|
||||
// Test navigating to the URL directly
|
||||
scrollToTop();
|
||||
expect(lastAnchorId).not.toBeInViewport();
|
||||
|
||||
browser.refresh();
|
||||
expect(lastAnchorId).toBeInViewport();
|
||||
expect(lastAnchorId).toHaveTop(yOffset);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not scroll "overzealously"', function() {
|
||||
var lastButton = buttons.last();
|
||||
var lastAnchor = anchors.last();
|
||||
var lastAnchorId = 'anchor-5';
|
||||
|
||||
// Make sure there is not enough room to scroll the last anchor to the top
|
||||
lastAnchor.getSize().then(function(size) {
|
||||
var tempHeight = size.height + (yOffset / 2);
|
||||
|
||||
execWithTempViewportHeight(tempHeight, function() {
|
||||
scrollIntoView(lastAnchorId);
|
||||
expect(lastAnchorId).toHaveTop(yOffset / 2);
|
||||
|
||||
lastButton.click();
|
||||
expect(lastAnchorId).toBeInViewport();
|
||||
expect(lastAnchorId).toHaveTop(yOffset);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Helpers
|
||||
function _script_getTop(id) {
|
||||
var elem = document.getElementById(id);
|
||||
var rect = elem.getBoundingClientRect();
|
||||
|
||||
return rect.top;
|
||||
}
|
||||
|
||||
function _script_isInViewport(id) {
|
||||
var elem = document.getElementById(id);
|
||||
var rect = elem.getBoundingClientRect();
|
||||
var docElem = document.documentElement;
|
||||
|
||||
return (rect.top < docElem.clientHeight) &&
|
||||
(rect.bottom > 0) &&
|
||||
(rect.left < docElem.clientWidth) &&
|
||||
(rect.right > 0);
|
||||
}
|
||||
|
||||
function execWithTempViewportHeight(tempHeight, fn) {
|
||||
setViewportHeight(tempHeight).then(function(oldHeight) {
|
||||
fn();
|
||||
setViewportHeight(oldHeight);
|
||||
});
|
||||
}
|
||||
|
||||
function scrollIntoView(id) {
|
||||
browser.driver.executeScript('document.getElementById("' + id + '").scrollIntoView()');
|
||||
}
|
||||
|
||||
function scrollToTop() {
|
||||
browser.driver.executeScript('window.scrollTo(0, 0)');
|
||||
}
|
||||
|
||||
function setViewportHeight(newHeight) {
|
||||
return browser.driver.
|
||||
executeScript('return document.documentElement.clientHeight').
|
||||
then(function(oldHeight) {
|
||||
var heightDiff = newHeight - oldHeight;
|
||||
var win = browser.driver.manage().window();
|
||||
|
||||
return win.getSize().then(function(size) {
|
||||
return win.
|
||||
setSize(size.width, size.height + heightDiff).
|
||||
then(function() { return oldHeight; });
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,10 +1,9 @@
|
||||
describe('App where angular is loaded more than once', function() {
|
||||
beforeEach(function() {
|
||||
loadFixture("angular-already-loaded").andWaitForAngular();
|
||||
loadFixture('angular-already-loaded');
|
||||
});
|
||||
|
||||
it('should have the interpolated text', function() {
|
||||
expect(element(by.binding('text')).getText())
|
||||
.toBe('Hello, world!');
|
||||
expect(element(by.binding('text')).getText()).toBe('Hello, world!');
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,4 @@
|
||||
var helper = {
|
||||
andWaitForAngular: function() {
|
||||
browser.waitForAngular();
|
||||
},
|
||||
loadFixture: function(fixture) {
|
||||
var i = 0;
|
||||
while (fixture[i] === '/') ++i;
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
describe('angular-loader', function() {
|
||||
beforeEach(function() {
|
||||
loadFixture('loader');
|
||||
});
|
||||
|
||||
it('should not be broken by loading the modules before core', function() {
|
||||
expect(element(by.binding('text')).getText()).toBe('Hello, world!');
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,11 @@
|
||||
describe('Customizing the jqlite / jquery version', function() {
|
||||
|
||||
it('should be able to force jqlite', function() {
|
||||
loadFixture("ngJq").andWaitForAngular();
|
||||
describe('Customizing the jqLite / jQuery version', function() {
|
||||
it('should be able to force jqLite', function() {
|
||||
loadFixture('ng-jq');
|
||||
expect(element(by.binding('jqueryVersion')).getText()).toBe('jqLite');
|
||||
});
|
||||
|
||||
it('should be able to use a specific version jQuery', function() {
|
||||
loadFixture("ngJqJquery").andWaitForAngular();
|
||||
loadFixture('ng-jq-jquery');
|
||||
expect(element(by.binding('jqueryVersion')).getText()).toBe('2.1.0');
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,10 @@
|
||||
// Sample E2E test:
|
||||
//
|
||||
describe('Sample', function() {
|
||||
beforeEach(function() {
|
||||
loadFixture("sample").andWaitForAngular();
|
||||
loadFixture('sample');
|
||||
});
|
||||
|
||||
it('should have the interpolated text', function() {
|
||||
expect(element(by.binding('text')).getText())
|
||||
.toBe('Hello, world!');
|
||||
expect(element(by.binding('text')).getText()).toBe('Hello, world!');
|
||||
});
|
||||
});
|
||||
@@ -3816,6 +3816,96 @@ describe('$compile', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('$doCheck', function() {
|
||||
it('should call `$doCheck`, if provided, for each digest cycle, after $onChanges and $onInit', function() {
|
||||
var log = [];
|
||||
|
||||
function TestController() { }
|
||||
TestController.prototype.$doCheck = function() { log.push('$doCheck'); };
|
||||
TestController.prototype.$onChanges = function() { log.push('$onChanges'); };
|
||||
TestController.prototype.$onInit = function() { log.push('$onInit'); };
|
||||
|
||||
angular.module('my', [])
|
||||
.component('dcc', {
|
||||
controller: TestController,
|
||||
bindings: { 'prop1': '<' }
|
||||
});
|
||||
|
||||
module('my');
|
||||
inject(function($compile, $rootScope) {
|
||||
element = $compile('<dcc prop1="val"></dcc>')($rootScope);
|
||||
expect(log).toEqual([
|
||||
'$onChanges',
|
||||
'$onInit',
|
||||
'$doCheck'
|
||||
]);
|
||||
|
||||
// Clear log
|
||||
log = [];
|
||||
|
||||
$rootScope.$apply();
|
||||
expect(log).toEqual([
|
||||
'$doCheck',
|
||||
'$doCheck'
|
||||
]);
|
||||
|
||||
// Clear log
|
||||
log = [];
|
||||
|
||||
$rootScope.$apply('val = 2');
|
||||
expect(log).toEqual([
|
||||
'$doCheck',
|
||||
'$onChanges',
|
||||
'$doCheck'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work if $doCheck is provided in the constructor', function() {
|
||||
var log = [];
|
||||
|
||||
function TestController() {
|
||||
this.$doCheck = function() { log.push('$doCheck'); };
|
||||
this.$onChanges = function() { log.push('$onChanges'); };
|
||||
this.$onInit = function() { log.push('$onInit'); };
|
||||
}
|
||||
|
||||
angular.module('my', [])
|
||||
.component('dcc', {
|
||||
controller: TestController,
|
||||
bindings: { 'prop1': '<' }
|
||||
});
|
||||
|
||||
module('my');
|
||||
inject(function($compile, $rootScope) {
|
||||
element = $compile('<dcc prop1="val"></dcc>')($rootScope);
|
||||
expect(log).toEqual([
|
||||
'$onChanges',
|
||||
'$onInit',
|
||||
'$doCheck'
|
||||
]);
|
||||
|
||||
// Clear log
|
||||
log = [];
|
||||
|
||||
$rootScope.$apply();
|
||||
expect(log).toEqual([
|
||||
'$doCheck',
|
||||
'$doCheck'
|
||||
]);
|
||||
|
||||
// Clear log
|
||||
log = [];
|
||||
|
||||
$rootScope.$apply('val = 2');
|
||||
expect(log).toEqual([
|
||||
'$doCheck',
|
||||
'$onChanges',
|
||||
'$doCheck'
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('$onChanges', function() {
|
||||
|
||||
@@ -7889,7 +7979,7 @@ describe('$compile', function() {
|
||||
directive('trans', function() {
|
||||
return {
|
||||
transclude: true,
|
||||
template: '<div ng-transclude>old stuff! </div>'
|
||||
template: '<div ng-transclude>old stuff!</div>'
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -7906,18 +7996,152 @@ describe('$compile', function() {
|
||||
directive('trans', function() {
|
||||
return {
|
||||
transclude: true,
|
||||
template: '<div ng-transclude>old stuff! </div>'
|
||||
template: '<div ng-transclude>old stuff!</div>'
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function(log, $rootScope, $compile) {
|
||||
element = $compile('<div trans></div>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(sortedHtml(element.html())).toEqual('<div ng-transclude="">old stuff! </div>');
|
||||
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""><span>old stuff!</span></div>');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should clear the fallback content from the element during compile and before linking', function() {
|
||||
module(function() {
|
||||
directive('trans', function() {
|
||||
return {
|
||||
transclude: true,
|
||||
template: '<div ng-transclude>fallback content</div>'
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function(log, $rootScope, $compile) {
|
||||
element = jqLite('<div trans></div>');
|
||||
var linkfn = $compile(element);
|
||||
expect(element.html()).toEqual('<div ng-transclude=""></div>');
|
||||
linkfn($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""><span>fallback content</span></div>');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should allow cloning of the fallback via ngRepeat', function() {
|
||||
module(function() {
|
||||
directive('trans', function() {
|
||||
return {
|
||||
transclude: true,
|
||||
template: '<div ng-repeat="i in [0,1,2]"><div ng-transclude>{{i}}</div></div>'
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function(log, $rootScope, $compile) {
|
||||
element = $compile('<div trans></div>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(element.text()).toEqual('012');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should not link the fallback content if transcluded content is provided', function() {
|
||||
var linkSpy = jasmine.createSpy('postlink');
|
||||
|
||||
module(function() {
|
||||
directive('inner', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: 'old stuff! ',
|
||||
link: linkSpy
|
||||
};
|
||||
});
|
||||
|
||||
directive('trans', function() {
|
||||
return {
|
||||
transclude: true,
|
||||
template: '<div ng-transclude><inner></inner></div>'
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($rootScope, $compile) {
|
||||
element = $compile('<div trans>unicorn!</div>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""><span>unicorn!</span></div>');
|
||||
expect(linkSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should compile and link the fallback content if no transcluded content is provided', function() {
|
||||
var linkSpy = jasmine.createSpy('postlink');
|
||||
|
||||
module(function() {
|
||||
directive('inner', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: 'old stuff! ',
|
||||
link: linkSpy
|
||||
};
|
||||
});
|
||||
|
||||
directive('trans', function() {
|
||||
return {
|
||||
transclude: true,
|
||||
template: '<div ng-transclude><inner></inner></div>'
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function(log, $rootScope, $compile) {
|
||||
element = $compile('<div trans></div>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""><inner>old stuff! </inner></div>');
|
||||
expect(linkSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should compile and link the fallback content if an optional transclusion slot is not provided', function() {
|
||||
var linkSpy = jasmine.createSpy('postlink');
|
||||
|
||||
module(function() {
|
||||
directive('inner', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
template: 'old stuff! ',
|
||||
link: linkSpy
|
||||
};
|
||||
});
|
||||
|
||||
directive('trans', function() {
|
||||
return {
|
||||
transclude: { optionalSlot: '?optional'},
|
||||
template: '<div ng-transclude="optionalSlot"><inner></inner></div>'
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function(log, $rootScope, $compile) {
|
||||
element = $compile('<div trans></div>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(sortedHtml(element.html())).toEqual('<div ng-transclude="optionalSlot"><inner>old stuff! </inner></div>');
|
||||
expect(linkSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should cope if there is neither transcluded content nor fallback content', function() {
|
||||
module(function() {
|
||||
directive('trans', function() {
|
||||
return {
|
||||
transclude: true,
|
||||
template: '<div ng-transclude></div>'
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($rootScope, $compile) {
|
||||
element = $compile('<div trans></div>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(sortedHtml(element.html())).toEqual('<div ng-transclude=""></div>');
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw on an ng-transclude element inside no transclusion directive', function() {
|
||||
inject(function($rootScope, $compile) {
|
||||
// we need to do this because different browsers print empty attributes differently
|
||||
@@ -9716,37 +9940,6 @@ describe('$compile', function() {
|
||||
expect(element.children().eq(2).text()).toEqual('dorothy');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not overwrite the contents of an `ng-transclude` element, if the matching optional slot is not filled', function() {
|
||||
module(function() {
|
||||
directive('minionComponent', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: {},
|
||||
transclude: {
|
||||
minionSlot: 'minion',
|
||||
bossSlot: '?boss'
|
||||
},
|
||||
template:
|
||||
'<div class="boss" ng-transclude="bossSlot">default boss content</div>' +
|
||||
'<div class="minion" ng-transclude="minionSlot">default minion content</div>' +
|
||||
'<div class="other" ng-transclude>default content</div>'
|
||||
};
|
||||
});
|
||||
});
|
||||
inject(function($rootScope, $compile) {
|
||||
element = $compile(
|
||||
'<minion-component>' +
|
||||
'<minion>stuart</minion>' +
|
||||
'<span>dorothy</span>' +
|
||||
'<minion>kevin</minion>' +
|
||||
'</minion-component>')($rootScope);
|
||||
$rootScope.$apply();
|
||||
expect(element.children().eq(0).text()).toEqual('default boss content');
|
||||
expect(element.children().eq(1).text()).toEqual('stuartkevin');
|
||||
expect(element.children().eq(2).text()).toEqual('dorothy');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -1789,6 +1789,87 @@ describe('ngOptions', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should group if the group has a falsy value (except undefined)', function() {
|
||||
createSelect({
|
||||
'ng-model': 'selected',
|
||||
'ng-options': 'item.name group by item.group for item in values'
|
||||
});
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.values = [{name: 'A'},
|
||||
{name: 'B', group: ''},
|
||||
{name: 'C', group: null},
|
||||
{name: 'D', group: false},
|
||||
{name: 'E', group: 0}];
|
||||
scope.selected = scope.values[0];
|
||||
});
|
||||
|
||||
var optgroups = element.find('optgroup');
|
||||
var options = element.find('option');
|
||||
|
||||
expect(optgroups.length).toEqual(4);
|
||||
expect(options.length).toEqual(5);
|
||||
|
||||
expect(optgroups[0].label).toBe('');
|
||||
expect(optgroups[1].label).toBe('null');
|
||||
expect(optgroups[2].label).toBe('false');
|
||||
expect(optgroups[3].label).toBe('0');
|
||||
|
||||
expect(options[0].textContent).toBe('A');
|
||||
expect(options[0].parentNode).toBe(element[0]);
|
||||
|
||||
expect(options[1].textContent).toBe('B');
|
||||
expect(options[1].parentNode).toBe(optgroups[0]);
|
||||
|
||||
expect(options[2].textContent).toBe('C');
|
||||
expect(options[2].parentNode).toBe(optgroups[1]);
|
||||
|
||||
expect(options[3].textContent).toBe('D');
|
||||
expect(options[3].parentNode).toBe(optgroups[2]);
|
||||
|
||||
expect(options[4].textContent).toBe('E');
|
||||
expect(options[4].parentNode).toBe(optgroups[3]);
|
||||
});
|
||||
|
||||
|
||||
it('should not duplicate a group with a falsy value when the options are updated', function() {
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.values = [{value: 'A', group: ''},
|
||||
{value: 'B', group: 'First'}];
|
||||
scope.selected = scope.values[0];
|
||||
});
|
||||
|
||||
createSelect({
|
||||
'ng-model': 'selected',
|
||||
'ng-options': 'item.value group by item.group for item in values'
|
||||
});
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.values.push({value: 'C', group: false});
|
||||
});
|
||||
|
||||
var optgroups = element.find('optgroup');
|
||||
var options = element.find('option');
|
||||
|
||||
expect(optgroups.length).toEqual(3);
|
||||
expect(options.length).toEqual(3);
|
||||
|
||||
expect(optgroups[0].label).toBe('');
|
||||
expect(optgroups[1].label).toBe('First');
|
||||
expect(optgroups[2].label).toBe('false');
|
||||
|
||||
expect(options[0].textContent).toBe('A');
|
||||
expect(options[0].parentNode).toBe(optgroups[0]);
|
||||
|
||||
expect(options[1].textContent).toBe('B');
|
||||
expect(options[1].parentNode).toBe(optgroups[1]);
|
||||
|
||||
expect(options[2].textContent).toBe('C');
|
||||
expect(options[2].parentNode).toBe(optgroups[2]);
|
||||
});
|
||||
|
||||
|
||||
it('should bind to scope value and track/identify objects', function() {
|
||||
createSelect({
|
||||
'ng-model': 'selected',
|
||||
|
||||
@@ -192,6 +192,25 @@ describe('Filter: filter', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should allow specifying the special "match-all" property', function() {
|
||||
var items = [
|
||||
{foo: 'baz'},
|
||||
{bar: 'baz'},
|
||||
{'%': 'no dollar'}
|
||||
];
|
||||
|
||||
expect(filter(items, {$: 'baz'}).length).toBe(2);
|
||||
expect(filter(items, {$: 'baz'}, null, '%').length).toBe(0);
|
||||
|
||||
expect(filter(items, {'%': 'dollar'}).length).toBe(1);
|
||||
expect(filter(items, {$: 'dollar'}).length).toBe(1);
|
||||
expect(filter(items, {$: 'dollar'}, null, '%').length).toBe(0);
|
||||
|
||||
expect(filter(items, {'%': 'baz'}).length).toBe(0);
|
||||
expect(filter(items, {'%': 'baz'}, null, '%').length).toBe(2);
|
||||
});
|
||||
|
||||
|
||||
it('should match any properties in the nested object for given deep "$" property', function() {
|
||||
var items = [{person: {name: 'Annet', email: 'annet@example.com'}},
|
||||
{person: {name: 'Billy', email: 'me@billy.com'}},
|
||||
@@ -425,6 +444,7 @@ describe('Filter: filter', function() {
|
||||
toThrowMinErr('filter', 'notarray', 'Expected array but received: {"toString":null,"valueOf":null}');
|
||||
});
|
||||
|
||||
|
||||
it('should not throw an error if used with an array like object', function() {
|
||||
function getArguments() {
|
||||
return arguments;
|
||||
@@ -439,7 +459,6 @@ describe('Filter: filter', function() {
|
||||
expect(filter(argsObj, 'i').length).toBe(2);
|
||||
expect(filter('abc','b').length).toBe(1);
|
||||
expect(filter(nodeList, nodeFilterPredicate).length).toBe(1);
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
|
||||
describe('$httpBackend', function() {
|
||||
|
||||
var $backend, $browser, callbacks,
|
||||
var $backend, $browser, $jsonpCallbacks,
|
||||
xhr, fakeDocument, callback;
|
||||
|
||||
|
||||
beforeEach(inject(function($injector) {
|
||||
callbacks = {counter: 0};
|
||||
|
||||
$browser = $injector.get('$browser');
|
||||
|
||||
fakeDocument = {
|
||||
$$scripts: [],
|
||||
createElement: jasmine.createSpy('createElement').and.callFake(function() {
|
||||
@@ -28,7 +28,27 @@ describe('$httpBackend', function() {
|
||||
})
|
||||
}
|
||||
};
|
||||
$backend = createHttpBackend($browser, createMockXhr, $browser.defer, callbacks, fakeDocument);
|
||||
|
||||
$jsonpCallbacks = {
|
||||
createCallback: function(url) {
|
||||
$jsonpCallbacks[url] = function(data) {
|
||||
$jsonpCallbacks[url].called = true;
|
||||
$jsonpCallbacks[url].data = data;
|
||||
};
|
||||
return url;
|
||||
},
|
||||
wasCalled: function(callbackPath) {
|
||||
return $jsonpCallbacks[callbackPath].called;
|
||||
},
|
||||
getResponse: function(callbackPath) {
|
||||
return $jsonpCallbacks[callbackPath].data;
|
||||
},
|
||||
removeCallback: function(callbackPath) {
|
||||
delete $jsonpCallbacks[callbackPath];
|
||||
}
|
||||
};
|
||||
|
||||
$backend = createHttpBackend($browser, createMockXhr, $browser.defer, $jsonpCallbacks, fakeDocument);
|
||||
callback = jasmine.createSpy('done');
|
||||
}));
|
||||
|
||||
@@ -235,7 +255,7 @@ describe('$httpBackend', function() {
|
||||
|
||||
it('should call $xhrFactory with method and url', function() {
|
||||
var mockXhrFactory = jasmine.createSpy('mockXhrFactory').and.callFake(createMockXhr);
|
||||
$backend = createHttpBackend($browser, mockXhrFactory, $browser.defer, callbacks, fakeDocument);
|
||||
$backend = createHttpBackend($browser, mockXhrFactory, $browser.defer, $jsonpCallbacks, fakeDocument);
|
||||
$backend('GET', '/some-url', 'some-data', noop);
|
||||
expect(mockXhrFactory).toHaveBeenCalledWith('GET', '/some-url');
|
||||
});
|
||||
@@ -294,7 +314,7 @@ describe('$httpBackend', function() {
|
||||
|
||||
describe('JSONP', function() {
|
||||
|
||||
var SCRIPT_URL = /([^\?]*)\?cb=angular\.callbacks\.(.*)/;
|
||||
var SCRIPT_URL = /([^\?]*)\?cb=(.*)/;
|
||||
|
||||
|
||||
it('should add script tag for JSONP request', function() {
|
||||
@@ -310,7 +330,7 @@ describe('$httpBackend', function() {
|
||||
url = script.src.match(SCRIPT_URL);
|
||||
|
||||
expect(url[1]).toBe('http://example.org/path');
|
||||
callbacks[url[2]]('some-data');
|
||||
$jsonpCallbacks[url[2]]('some-data');
|
||||
browserTrigger(script, "load");
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
@@ -318,6 +338,8 @@ describe('$httpBackend', function() {
|
||||
|
||||
|
||||
it('should clean up the callback and remove the script', function() {
|
||||
spyOn($jsonpCallbacks, 'removeCallback').and.callThrough();
|
||||
|
||||
$backend('JSONP', 'http://example.org/path?cb=JSON_CALLBACK', null, callback);
|
||||
expect(fakeDocument.$$scripts.length).toBe(1);
|
||||
|
||||
@@ -325,10 +347,10 @@ describe('$httpBackend', function() {
|
||||
var script = fakeDocument.$$scripts.shift(),
|
||||
callbackId = script.src.match(SCRIPT_URL)[2];
|
||||
|
||||
callbacks[callbackId]('some-data');
|
||||
$jsonpCallbacks[callbackId]('some-data');
|
||||
browserTrigger(script, "load");
|
||||
|
||||
expect(callbacks[callbackId]).toBe(angular.noop);
|
||||
expect($jsonpCallbacks.removeCallback).toHaveBeenCalledOnceWith(callbackId);
|
||||
expect(fakeDocument.body.removeChild).toHaveBeenCalledOnceWith(script);
|
||||
});
|
||||
|
||||
@@ -343,7 +365,9 @@ describe('$httpBackend', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should abort request on timeout and replace callback with noop', function() {
|
||||
it('should abort request on timeout and remove JSONP callback', function() {
|
||||
spyOn($jsonpCallbacks, 'removeCallback').and.callThrough();
|
||||
|
||||
callback.and.callFake(function(status, response) {
|
||||
expect(status).toBe(-1);
|
||||
});
|
||||
@@ -359,7 +383,7 @@ describe('$httpBackend', function() {
|
||||
expect(fakeDocument.$$scripts.length).toBe(0);
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
|
||||
expect(callbacks[callbackId]).toBe(angular.noop);
|
||||
expect($jsonpCallbacks.removeCallback).toHaveBeenCalledOnceWith(callbackId);
|
||||
});
|
||||
|
||||
|
||||
|
||||