Compare commits
103 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f5e42e882 | |||
| d193c3a25c | |||
| 4da1cc3b81 | |||
| 6de08216e7 | |||
| 3c6e8ce044 | |||
| e51024ed54 | |||
| 04f1ebd470 | |||
| 2e63ab734a | |||
| 6333d65b76 | |||
| 1ce5d216c7 | |||
| 28c166939e | |||
| 14638f4a60 | |||
| 0c1fbdd242 | |||
| 33f7f26558 | |||
| e27eed3ca4 | |||
| 6cbbd96647 | |||
| d0cb69348e | |||
| f15f8df2b4 | |||
| e7662ebc31 | |||
| 8ceed4faf3 | |||
| 6903b5ec4c | |||
| 48e1f5605e | |||
| c5a3d8fc5f | |||
| c61149213b | |||
| ad7200e2c2 | |||
| bea74c0f56 | |||
| bacc3b7e0e | |||
| 8a1eb1625c | |||
| ed27e0ea6a | |||
| f81ff3beb0 | |||
| 720012eab6 | |||
| 3adfe5bda9 | |||
| dc0467879d | |||
| 528d7f9568 | |||
| 636ce70e47 | |||
| 5f5ee0f880 | |||
| 46b7cf7464 | |||
| 860edee65b | |||
| ebd0fbba8f | |||
| 093416f60f | |||
| 0400dc9c2a | |||
| 71fc3f4fa0 | |||
| 8caf1802e0 | |||
| 91b602263b | |||
| 0934b76b72 | |||
| 571bee7b2f | |||
| 11055132bf | |||
| 0898b1240b | |||
| 8dc09e6dab | |||
| 799353c75d | |||
| ffac747e84 | |||
| 998340de7f | |||
| 559313652e | |||
| a69251ab55 | |||
| 7e5248a33f | |||
| c210ff5eae | |||
| 9efb0d5ee9 | |||
| 0e622f7b5b | |||
| 071be60927 | |||
| a25aa5b577 | |||
| df9c720d08 | |||
| 4fe141f794 | |||
| f3b1d0b723 | |||
| 288225b080 | |||
| e967abcd30 | |||
| 2c3cd8126c | |||
| 41385f0afc | |||
| 500b0f6cdb | |||
| 40b27280ab | |||
| b73c64e2fb | |||
| 2f5d42ec72 | |||
| 25d731e9b0 | |||
| 15da7cc3dc | |||
| 34a6da24c1 | |||
| ebaa0f5985 | |||
| bb15d414c6 | |||
| f056036a4a | |||
| a055762027 | |||
| 82d38e4453 | |||
| 6a743f0b5b | |||
| 31f6f76291 | |||
| a2b5a5ed5f | |||
| 1d41e8a975 | |||
| 01182dfbb8 | |||
| e17f85cc5b | |||
| d7dc14dc0c | |||
| 578fa019b3 | |||
| 3c9096efb4 | |||
| 59273354b5 | |||
| f67204794e | |||
| b6389eedda | |||
| a6339d30d1 | |||
| 351fe4b79c | |||
| d19504a179 | |||
| e5e871fe1a | |||
| 90fa884f94 | |||
| 80b9018f29 | |||
| f6ac226c8b | |||
| 2d22380873 | |||
| 7c49d9986f | |||
| 209f4f3e0f | |||
| 5d68c763e2 | |||
| 3ef529806f |
@@ -1,15 +0,0 @@
|
||||
// This is an incomplete TODO list of checks we want to start enforcing
|
||||
//
|
||||
// The goal is to enable these checks one by one by moving them to .jscs.json along with commits
|
||||
// that correct the existing code base issues and make the new check pass.
|
||||
|
||||
{
|
||||
"validateParameterSeparator": ", ", // Re-assert this rule when JSCS allows multiple spaces
|
||||
"requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"],
|
||||
"disallowImplicitTypeConversion": ["string"],
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"validateJSDoc": {
|
||||
"checkParamNames": true,
|
||||
"requireParamTypes": true
|
||||
}
|
||||
}
|
||||
+155
-2
@@ -1,3 +1,130 @@
|
||||
<a name="1.4.1"></a>
|
||||
# 1.4.1 hyperionic-illumination (2015-06-16)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- workaround for IE11 MutationObserver
|
||||
([f3b1d0b7](https://github.com/angular/angular.js/commit/f3b1d0b723298a5f8ea21d0704405649cce1b5fc),
|
||||
[#11781](https://github.com/angular/angular.js/issues/11781))
|
||||
- prevent exception when using `watch` as isolated scope binding property in Firefox
|
||||
([a6339d30](https://github.com/angular/angular.js/commit/a6339d30d1379689da5eec9647a953f64821f8b0),
|
||||
[#11627](https://github.com/angular/angular.js/issues/11627))
|
||||
- assign controller return values correctly for multiple directives
|
||||
([8caf1802](https://github.com/angular/angular.js/commit/8caf1802e0e93389dec626ef35e04a302aa6c39d),
|
||||
[#12029](https://github.com/angular/angular.js/issues/12029), [#12036](https://github.com/angular/angular.js/issues/12036))
|
||||
- **$location:** do not get caught in infinite digest in IE9 when redirecting in `$locationChangeSuccess`
|
||||
([91b60226](https://github.com/angular/angular.js/commit/91b602263b96b6fce1331208462e18eb647f4d60),
|
||||
[#11439](https://github.com/angular/angular.js/issues/11439), [#11675](https://github.com/angular/angular.js/issues/11675), [#11935](https://github.com/angular/angular.js/issues/11935), [#12083](https://github.com/angular/angular.js/issues/12083))
|
||||
- **$parse:** set null reference properties to `undefined`
|
||||
([71fc3f4f](https://github.com/angular/angular.js/commit/71fc3f4fa0cd12eff335d57efed7c033554749f4),
|
||||
[#12099](https://github.com/angular/angular.js/issues/12099))
|
||||
([d19504a1](https://github.com/angular/angular.js/commit/d19504a179355d7801d59a8db0285a1322e04601),
|
||||
[#11959](https://github.com/angular/angular.js/issues/11959))
|
||||
- **$sanitize:** do not remove `tabindex` attribute
|
||||
([799353c7](https://github.com/angular/angular.js/commit/799353c75de28e6fbf52dac6e0721e85b578575a),
|
||||
[#8371](https://github.com/angular/angular.js/issues/8371), [#5853](https://github.com/angular/angular.js/issues/5853))
|
||||
- **copy:** do not copy the same object twice
|
||||
([0e622f7b](https://github.com/angular/angular.js/commit/0e622f7b5bc3d5d0ab0fbc1a1bc69404bd7216d5))
|
||||
- **forms:** parse exponential notation in `numberInputType` directive
|
||||
([ebd0fbba](https://github.com/angular/angular.js/commit/ebd0fbba8ff90bee0cd016d574643d56a7f81ed0),
|
||||
[#12121](https://github.com/angular/angular.js/issues/12121), [#12122](https://github.com/angular/angular.js/issues/12122))
|
||||
- **linky:** allow case insensitive scheme detection
|
||||
([8dc09e6d](https://github.com/angular/angular.js/commit/8dc09e6dabb84c2c611cdc9e40adfac989648200),
|
||||
[#12073](https://github.com/angular/angular.js/issues/12073), [#12073](https://github.com/angular/angular.js/issues/12073))
|
||||
- **ngAria:**
|
||||
- update `aria-valuemin/max` when `min/max` change
|
||||
([ebaa0f59](https://github.com/angular/angular.js/commit/ebaa0f598501702ae64d59ada0ae492eaf0e2db6),
|
||||
[#11770](https://github.com/angular/angular.js/issues/11770), [#11774](https://github.com/angular/angular.js/issues/11774))
|
||||
- ensure boolean values for aria-hidden and aria-disabled
|
||||
([59273354](https://github.com/angular/angular.js/commit/59273354b57dd8d1ad2cd2f4740ffa8923e480f9),
|
||||
[#11365](https://github.com/angular/angular.js/issues/11365))
|
||||
- **ngModel:** ignore Object.prototype properties on the form validation object
|
||||
([0934b76b](https://github.com/angular/angular.js/commit/0934b76b72cec86093414834ac4cb7f0946b651d),
|
||||
[#12066](https://github.com/angular/angular.js/issues/12066))
|
||||
- **ngOptions:**
|
||||
- do not watch properties starting with $
|
||||
([34a6da24](https://github.com/angular/angular.js/commit/34a6da24c17356d4ffc70aec3f621a140a9a61ab),
|
||||
[#11930](https://github.com/angular/angular.js/issues/11930), [#12010](https://github.com/angular/angular.js/issues/12010))
|
||||
- use reference check only when not using trackBy
|
||||
([d7dc14dc](https://github.com/angular/angular.js/commit/d7dc14dc0cdeb9c187d227e19acc8aca7df9d740),
|
||||
[#11936](https://github.com/angular/angular.js/issues/11936), [#11996](https://github.com/angular/angular.js/issues/11996))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:** show module name during `multidir` error
|
||||
([351fe4b7](https://github.com/angular/angular.js/commit/351fe4b79c50a45a11af2fcd2aa7b6fd3b70058d),
|
||||
[#11775](https://github.com/angular/angular.js/issues/11775))
|
||||
- **$q:** $q.resolve as an alias for $q.when
|
||||
([3ef52980](https://github.com/angular/angular.js/commit/3ef529806fef28b41ca4af86a330f39a95699cf6),
|
||||
[#11944](https://github.com/angular/angular.js/issues/11944), [#11987](https://github.com/angular/angular.js/issues/11987))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$compile:** avoid jquery data calls when there is no data
|
||||
([9efb0d5e](https://github.com/angular/angular.js/commit/9efb0d5ee961b57c8fc144a3138a15955e4010e2))
|
||||
|
||||
|
||||
|
||||
<a name="1.3.16"></a>
|
||||
# 1.3.16 cookie-oatmealification (2015-06-05)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:** throw error on invalid directive name
|
||||
([634e4671](https://github.com/angular/angular.js/commit/634e467172efa696eb32ef8942ffbedeecbd030e),
|
||||
[#11281](https://github.com/angular/angular.js/issues/11281), [#11109](https://github.com/angular/angular.js/issues/11109))
|
||||
- **$cookies:** update $cookies to prevent duplicate cookie writes and play nice with external code
|
||||
([706a93ab](https://github.com/angular/angular.js/commit/706a93ab6960e3474698ccf9a8048b3c32e567c6),
|
||||
[#11490](https://github.com/angular/angular.js/issues/11490), [#11515](https://github.com/angular/angular.js/issues/11515))
|
||||
- **$http:** throw error if `success` and `error` methods do not receive a function
|
||||
([731e1f65](https://github.com/angular/angular.js/commit/731e1f6534ab7fd1e053b8d7a25c902fcd934fea),
|
||||
[#11330](https://github.com/angular/angular.js/issues/11330), [#11333](https://github.com/angular/angular.js/issues/11333))
|
||||
- **core:** ensure that multiple requests to requestAnimationFrame are buffered
|
||||
([0adc0364](https://github.com/angular/angular.js/commit/0adc0364265b06c567ccc8e90a7f09cc46f235b2),
|
||||
[#11791](https://github.com/angular/angular.js/issues/11791))
|
||||
- **filterFilter:** fix matching against `null`/`undefined`
|
||||
([9dd0fe35](https://github.com/angular/angular.js/commit/9dd0fe35d1027e59b84b2396abee00d8683f3b50),
|
||||
[#11573](https://github.com/angular/angular.js/issues/11573), [#11617](https://github.com/angular/angular.js/issues/11617))
|
||||
- **jqLite:**
|
||||
- check for "length" in obj in isArrayLike to prevent iOS8 JIT bug from surfacing
|
||||
([647f3f55](https://github.com/angular/angular.js/commit/647f3f55eb7100a255272f7277f0f962de234a32),
|
||||
[#11508](https://github.com/angular/angular.js/issues/11508))
|
||||
- attr should ignore comment, text and attribute nodes
|
||||
([181e5ebc](https://github.com/angular/angular.js/commit/181e5ebc3fce5312feacaeace4fcad0d32f4d73c))
|
||||
- **ngAnimate:**
|
||||
- ensure that minified repaint code isn't removed
|
||||
([d5c99ea4](https://github.com/angular/angular.js/commit/d5c99ea42b834343fd0362cfc572f47e7536ccfb),
|
||||
[#9936](https://github.com/angular/angular.js/issues/9936))
|
||||
- **ngAria:** handle elements with role="checkbox/menuitemcheckbox"
|
||||
([1c282af5](https://github.com/angular/angular.js/commit/1c282af5abc205d4aac37c05c5cb725d71747134),
|
||||
[#11317](https://github.com/angular/angular.js/issues/11317), [#11321](https://github.com/angular/angular.js/issues/11321))
|
||||
- **ngModel:** allow setting model to NaN when asyncValidator is present
|
||||
([b64519fe](https://github.com/angular/angular.js/commit/b64519fea7f1a5ec75e32c4b71b012b827314153),
|
||||
[#11315](https://github.com/angular/angular.js/issues/11315), [#11411](https://github.com/angular/angular.js/issues/11411))
|
||||
- **ngTouch:**
|
||||
- check undefined tagName for SVG event target
|
||||
([7560a8d2](https://github.com/angular/angular.js/commit/7560a8d2d65955ddb60ede9d586502f4e3cbd062))
|
||||
- register touches properly when jQuery is used
|
||||
([40441f6d](https://github.com/angular/angular.js/commit/40441f6dfc5ebd5cdc679c269c4639238f5351eb),
|
||||
[#4001](https://github.com/angular/angular.js/issues/4001), [#8584](https://github.com/angular/angular.js/issues/8584), [#10797](https://github.com/angular/angular.js/issues/10797), [#11488](https://github.com/angular/angular.js/issues/11488))
|
||||
- **select:** prevent unknown option being added to select when bound to null property
|
||||
([9e3f82bb](https://github.com/angular/angular.js/commit/9e3f82bbaf83cad7bb3121db756099b0880562e6),
|
||||
[#11872](https://github.com/angular/angular.js/issues/11872), [#11875](https://github.com/angular/angular.js/issues/11875))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **travis:** run unit tests on iOS 8
|
||||
([1f650871](https://github.com/angular/angular.js/commit/1f650871266b88b3dab4a894a839a82ac9a06b69),
|
||||
[#11479](https://github.com/angular/angular.js/issues/11479))
|
||||
|
||||
|
||||
|
||||
<a name="1.4.0"></a>
|
||||
# 1.4.0 jaracimrman-existence (2015-05-26)
|
||||
|
||||
@@ -374,7 +501,7 @@ To get the desired behaviour you need to iterate using the object form of the `n
|
||||
## Breaking Changes
|
||||
|
||||
- **$animate:** due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
|
||||
JavaSript and CSS animations can no longer be run in
|
||||
JavaScript and CSS animations can no longer be run in
|
||||
parallel. With earlier versions of ngAnimate, both CSS and JS animations
|
||||
would be run together when multiple animations were detected. This
|
||||
feature has now been removed, however, the same effect, with even more
|
||||
@@ -420,7 +547,7 @@ $animate.off(element, 'enter', fn);
|
||||
|
||||
- **$animate:** due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
|
||||
There is no need to call `$scope.$apply` or
|
||||
`$scope.$digest` inside of a animation promise callback anymore
|
||||
`$scope.$digest` inside of an animation promise callback anymore
|
||||
since the promise is resolved within a digest automatically (but a
|
||||
digest is not run unless the promise is chained).
|
||||
|
||||
@@ -1153,7 +1280,33 @@ But in practice this is not what people want and so this change iterates over pr
|
||||
in the order they are returned by Object.keys(obj), which is almost always the order
|
||||
in which the properties were defined.
|
||||
|
||||
- **select:** due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
|
||||
|
||||
the `select` directive will now use strict comparison of the `ngModel` scope value against `option`
|
||||
values to determine which option is selected. This means `Number` scope values will not be matched
|
||||
against numeric option strings.
|
||||
In Angular 1.3.x, setting `scope.x = 200` would select the `option` with the value 200 in the following `select`:
|
||||
|
||||
```
|
||||
<select ng-model="x">
|
||||
<option value="100">100</option>
|
||||
<option value="200">200</option>
|
||||
</select>
|
||||
```
|
||||
|
||||
In Angular 1.4.x, the 'unknown option' will be selected.
|
||||
To remedy this, you can simply initialize the model as a string: `scope.x = '200'`, or if you want to
|
||||
keep the model as a `Number`, you can do the conversion via `$formatters` and `$parsers` on `ngModel`:
|
||||
|
||||
```js
|
||||
ngModelCtrl.$parsers.push(function(value) {
|
||||
return parseInt(value, 10); // Convert option value to number
|
||||
});
|
||||
|
||||
ngModelCtrl.$formatters.push(function(value) {
|
||||
return value.toString(); // Convert scope value to string
|
||||
});
|
||||
```
|
||||
|
||||
<a name="1.3.9"></a>
|
||||
# 1.3.9 multidimensional-awareness (2015-01-13)
|
||||
|
||||
+1
-1
@@ -158,7 +158,7 @@ module.exports = function(grunt) {
|
||||
jscs: {
|
||||
src: ['src/**/*.js', 'test/**/*.js'],
|
||||
options: {
|
||||
config: ".jscs.json"
|
||||
config: ".jscsrc"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
Using AngularJS with the Closure Compiler
|
||||
=========================================
|
||||
|
||||
The Closure Compiler project contains externs definitions for AngularJS
|
||||
JavaScript in its `contrib/externs` directory.
|
||||
The Closure Compiler project contains definitions for the AngularJS JavaScript
|
||||
in its `contrib/externs` directory.
|
||||
|
||||
The definitions contain externs for use with the Closure compiler (aka
|
||||
JSCompiler). Passing these files to the --externs parameter of a compiler
|
||||
|
||||
@@ -10,7 +10,7 @@ the browser how to do dependency injection and inversion of control.
|
||||
|
||||
Oh yeah and it helps with server-side communication, taming async callbacks with promises and
|
||||
deferreds. It also makes client-side navigation and deeplinking with hashbang urls or HTML5 pushState a
|
||||
piece of cake. The best of all: it makes development fun!
|
||||
piece of cake. Best of all?? It makes development fun!
|
||||
|
||||
* Web site: http://angularjs.org
|
||||
* Tutorial: http://docs.angularjs.org/tutorial
|
||||
|
||||
@@ -35,7 +35,7 @@ angular.module('tutorials', [])
|
||||
'step': '@docTutorialReset'
|
||||
},
|
||||
template:
|
||||
'<p><a href="" ng-click="show=!show;$event.stopPropagation()">Workspace Reset Instructions ➤</a></p>\n' +
|
||||
'<p><button class="btn" ng-click="show=!show">Workspace Reset Instructions ➤</button></p>\n' +
|
||||
'<div class="alert alert-info" ng-show="show">\n' +
|
||||
' <p>Reset the workspace to step {{step}}.</p>' +
|
||||
' <p><pre>git checkout -f step-{{step}}</pre></p>\n' +
|
||||
@@ -43,7 +43,7 @@ angular.module('tutorials', [])
|
||||
'<a href="http://angular.github.io/angular-phonecat/step-{{step}}/app">Step {{step}} Live Demo</a>.</p>\n' +
|
||||
'</div>\n' +
|
||||
'<p>The most important changes are listed below. You can see the full diff on ' +
|
||||
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{step ? (step - 1): \'0~1\'}}...step-{{step}}">GitHub</a>\n' +
|
||||
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{step ? (step - 1): \'0~1\'}}...step-{{step}}" title="See diff on Github">GitHub</a>\n' +
|
||||
'</p>'
|
||||
};
|
||||
});
|
||||
@@ -10,14 +10,14 @@ var Package = require('dgeni').Package;
|
||||
module.exports = new Package('angularjs', [
|
||||
require('dgeni-packages/ngdoc'),
|
||||
require('dgeni-packages/nunjucks'),
|
||||
require('dgeni-packages/examples')
|
||||
require('dgeni-packages/examples'),
|
||||
require('dgeni-packages/git')
|
||||
])
|
||||
|
||||
|
||||
.factory(require('./services/errorNamespaceMap'))
|
||||
.factory(require('./services/getMinerrInfo'))
|
||||
.factory(require('./services/getVersion'))
|
||||
.factory(require('./services/gitData'))
|
||||
|
||||
.factory(require('./services/deployments/debug'))
|
||||
.factory(require('./services/deployments/default'))
|
||||
@@ -26,7 +26,6 @@ module.exports = new Package('angularjs', [
|
||||
|
||||
.factory(require('./inline-tag-defs/type'))
|
||||
|
||||
|
||||
.processor(require('./processors/error-docs'))
|
||||
.processor(require('./processors/index-page'))
|
||||
.processor(require('./processors/keywords'))
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var versionInfo = require('../../../lib/versions/version-info');
|
||||
|
||||
/**
|
||||
* @dgService gitData
|
||||
* @description
|
||||
* Information from the local git repository
|
||||
*/
|
||||
module.exports = function gitData() {
|
||||
return {
|
||||
version: versionInfo.currentVersion,
|
||||
versions: versionInfo.previousVersions,
|
||||
info: versionInfo.gitRepoInfo
|
||||
};
|
||||
};
|
||||
@@ -76,7 +76,7 @@
|
||||
<div class="row">
|
||||
<div class="col-md-9 header-branding">
|
||||
<a class="brand navbar-brand" href="http://angularjs.org">
|
||||
<img width="117" height="30" class="logo" ng-src="img/angularjs-for-header-only.svg">
|
||||
<img width="117" height="30" class="logo" alt="Link to Angular JS Homepage" ng-src="img/angularjs-for-header-only.svg">
|
||||
</a>
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="divider-vertical"></li>
|
||||
@@ -223,7 +223,7 @@
|
||||
Super-powered by Google ©2010-2015
|
||||
( <a id="version"
|
||||
ng-href="https://github.com/angular/angular.js/blob/master/CHANGELOG.md#{{versionNumber}}"
|
||||
ng-bind-template="v{{version}}">
|
||||
ng-bind-template="v{{version}}" title="Changelog of this version of Angular JS">
|
||||
</a>
|
||||
)
|
||||
</p>
|
||||
|
||||
@@ -693,7 +693,7 @@ A path should always begin with forward slash (`/`); the `$location.path()` sett
|
||||
forward slash if it is missing.
|
||||
|
||||
Note that the `!` prefix in the hashbang mode is not part of `$location.path()`; it is actually
|
||||
hashPrefix.
|
||||
`hashPrefix`.
|
||||
|
||||
## Crawling your app
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ Try out the Live Preview above, and then let's walk through the example and desc
|
||||
This looks like normal HTML, with some new markup. In Angular, a file like this is called a
|
||||
<a name="template">{@link templates template}</a>. When Angular starts your application, it parses and
|
||||
processes this new markup from the template using the <a name="compiler">{@link compiler compiler}</a>.
|
||||
The loaded, transformed and rendered DOM is then called the <a name="view">view</a>.
|
||||
The loaded, transformed and rendered DOM is then called the <a name="view"></a>*view*.
|
||||
|
||||
The first kind of new markup are the <a name="directive">{@link directive directives}</a>.
|
||||
They apply special behavior to attributes or elements in the HTML. In the example above we use the
|
||||
@@ -79,7 +79,7 @@ An <a name="expression">{@link expression expression}</a> in a template is a Jav
|
||||
to read and write variables. Note that those variables are not global variables.
|
||||
Just like variables in a JavaScript function live in a scope,
|
||||
Angular provides a <a name="scope">{@link scope scope}</a> for the variables accessible to expressions.
|
||||
The values that are stored in variables on the scope are referred to as the <a name="model">model</a>
|
||||
The values that are stored in variables on the scope are referred to as the <a name="model"></a>*model*
|
||||
in the rest of the documentation.
|
||||
Applied to the example above, the markup directs Angular to "take the data we got from the input widgets
|
||||
and multiply them together".
|
||||
|
||||
@@ -51,7 +51,7 @@ In the following example, we say that the `<input>` element **matches** the `ngM
|
||||
The following also **matches** `ngModel`:
|
||||
|
||||
```html
|
||||
<input data-ng:model="foo">
|
||||
<input data-ng-model="foo">
|
||||
```
|
||||
|
||||
### Normalization
|
||||
|
||||
@@ -28,13 +28,13 @@ Angular expressions are like JavaScript expressions with the following differenc
|
||||
|
||||
* **No Control Flow Statements:** You cannot use the following in an Angular expression:
|
||||
conditionals, loops, or exceptions.
|
||||
|
||||
|
||||
* **No Function Declarations:** You cannot declare functions in an Angular expression,
|
||||
even inside `ng-init` directive.
|
||||
|
||||
* **No RegExp Creation With Literal Notation:** You cannot create regular expressions
|
||||
|
||||
* **No RegExp Creation With Literal Notation:** You cannot create regular expressions
|
||||
in an Angular expression.
|
||||
|
||||
|
||||
* **No Comma And Void Operators:** You cannot use `,` or `void` in an Angular expression.
|
||||
|
||||
* **Filters:** You can use {@link guide/filter filters} within expressions to format data before
|
||||
@@ -175,7 +175,7 @@ expression, delegate to a JavaScript method instead.
|
||||
## No function declarations or RegExp creation with literal notation
|
||||
|
||||
You can't declare functions or create regular expressions from within AngularJS expressions. This is
|
||||
to avoid complex model transformation logic inside templates. Such logic is better placed in a
|
||||
to avoid complex model transformation logic inside templates. Such logic is better placed in a
|
||||
controller or in a dedicated filter where it can be tested properly.
|
||||
|
||||
## `$event`
|
||||
@@ -303,19 +303,23 @@ then the expression is not fulfilled and will remain watched.
|
||||
keep dirty-checking the watch in the future digest loops by following the same
|
||||
algorithm starting from step 1
|
||||
|
||||
#### Special case for object literals
|
||||
|
||||
Unlike simple values, object-literals are watched until every key is defined.
|
||||
See http://www.bennadel.com/blog/2760-one-time-data-bindings-for-object-literal-expressions-in-angularjs-1-3.htm
|
||||
|
||||
### How to benefit from one-time binding
|
||||
|
||||
If the expression will not change once set, it is a candidate for one-time binding.
|
||||
If the expression will not change once set, it is a candidate for one-time binding.
|
||||
Here are three example cases.
|
||||
|
||||
When interpolating text or attributes:
|
||||
|
||||
```html
|
||||
<div name="attr: {{::color}}">text: {{::name}}</div>
|
||||
<div name="attr: {{::color}}">text: {{::name | uppercase}}</div>
|
||||
```
|
||||
|
||||
When using a directive with bidirectional binding and the parameters will not change:
|
||||
When using a directive with bidirectional binding and parameters that will not change:
|
||||
|
||||
```js
|
||||
someModule.directive('someDirective', function() {
|
||||
@@ -338,7 +342,6 @@ When using a directive that takes an expression:
|
||||
|
||||
```html
|
||||
<ul>
|
||||
<li ng-repeat="item in ::items">{{item.name}};</li>
|
||||
<li ng-repeat="item in ::items | orderBy:'name'">{{item.name}};</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
```
|
||||
@@ -491,9 +491,7 @@ The following example shows how to add two-way data-binding to contentEditable e
|
||||
link: function(scope, elm, attrs, ctrl) {
|
||||
// view -> model
|
||||
elm.on('blur', function() {
|
||||
scope.$apply(function() {
|
||||
ctrl.$setViewValue(elm.html());
|
||||
});
|
||||
ctrl.$setViewValue(elm.html());
|
||||
});
|
||||
|
||||
// model -> view
|
||||
|
||||
@@ -305,7 +305,7 @@ matching is **case-sensitive**.
|
||||
|
||||
#### Selection Keywords
|
||||
|
||||
Selection keywords are simple words like "male" and "female". The keyword, "other", and it's
|
||||
Selection keywords are simple words like "male" and "female". The keyword, "other", and its
|
||||
corresponding message are required while others are optional. It is used when the Angular
|
||||
expression does not match (case-insensitively) any of the other keywords specified.
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ Animations in 1.4 have been refactored internally, but the API has stayed much t
|
||||
some breaking changes that need to be addressed when upgrading to 1.4.
|
||||
|
||||
Due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
|
||||
JavaSript and CSS animations can no longer be run in
|
||||
JavaScript and CSS animations can no longer be run in
|
||||
parallel. With earlier versions of ngAnimate, both CSS and JS animations
|
||||
would be run together when multiple animations were detected. This
|
||||
feature has been removed, however, the same effect, with even more
|
||||
@@ -88,10 +88,10 @@ element.on('$animate:before', function(e, data) {
|
||||
element.off('$animate:before', fn);
|
||||
|
||||
// 1.4+
|
||||
$animate.on(element, 'enter', function(data) {
|
||||
$animate.on('enter', element, function(data) {
|
||||
//...
|
||||
});
|
||||
$animate.off(element, 'enter', fn);
|
||||
$animate.off('enter', element, fn);
|
||||
```
|
||||
|
||||
Due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
|
||||
@@ -136,7 +136,7 @@ class based animations (animations triggered via ngClass) in order to ensure tha
|
||||
|
||||
|
||||
|
||||
## Forms (`ngMessages`, `ngOptions`)
|
||||
## Forms (`ngMessages`, `ngOptions`, `select`)
|
||||
|
||||
### ngMessages
|
||||
The ngMessages module has also been subject to an internal refactor to allow it to be more flexible
|
||||
@@ -178,8 +178,8 @@ have been fixed. The breaking changes are comparatively minor and should not aff
|
||||
Due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
|
||||
when `ngOptions` renders the option values within the DOM, the resulting HTML code is different.
|
||||
Normally this should not affect your application at all, however, if your code relies on inspecting
|
||||
the value property of `<option>` elements (that `ngOptions` generates) then be sure to [read the details]
|
||||
(https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef).
|
||||
the value property of `<option>` elements (that `ngOptions` generates) then be sure
|
||||
to [read the details](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef).
|
||||
|
||||
Due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
|
||||
when iterating over an object's properties using the `(key, value) in obj` syntax
|
||||
@@ -190,6 +190,35 @@ in the order they are returned by Object.keys(obj), which is almost always the o
|
||||
in which the properties were defined.
|
||||
|
||||
|
||||
### select
|
||||
|
||||
Due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
|
||||
the `select` directive will now use strict comparison of the `ngModel` scope value against `option`
|
||||
values to determine which option is selected. This means `Number` scope values will not be matched
|
||||
against numeric option strings.
|
||||
In Angular 1.3.x, setting `scope.x = 200` would select the option with the value 200 in the following `select`:
|
||||
|
||||
```
|
||||
<select ng-model="x">
|
||||
<option value="100">100</option>
|
||||
<option value="200">200</option>
|
||||
</select>
|
||||
```
|
||||
|
||||
In Angular 1.4.x, the 'unknown option' will be selected.
|
||||
To remedy this, you can simply initialize the model as a string: `scope.x = '200'`, or if you want to
|
||||
keep the model as a `Number`, you can do the conversion via `$formatters` and `$parsers` on `ngModel`:
|
||||
|
||||
```js
|
||||
ngModelCtrl.$parsers.push(function(value) {
|
||||
return parseInt(value, 10); // Convert option value to number
|
||||
});
|
||||
|
||||
ngModelCtrl.$formatters.push(function(value) {
|
||||
return value.toString(); // Convert scope value to string
|
||||
});
|
||||
```
|
||||
|
||||
## Templating (`ngRepeat`, `$compile`)
|
||||
|
||||
### ngRepeat
|
||||
@@ -217,6 +246,9 @@ Due to [6a38dbfd](https://github.com/angular/angular.js/commit/6a38dbfd3c34c8f9e
|
||||
previously, '&' expressions would always set up a function in the isolate scope. Now, if the binding
|
||||
is marked as optional and the attribute is not specified, no function will be added to the isolate scope.
|
||||
|
||||
Due to [62d514b](https://github.com/angular/angular.js/commit/62d514b06937cc7dd86e973ea11165c88343b42d),
|
||||
returning an object from a controller constructor function will now override the scope. Views that use the
|
||||
controllerAs method will no longer get the this reference, but the returned object.
|
||||
|
||||
|
||||
## Cookies (`ngCookies`)
|
||||
|
||||
@@ -140,7 +140,7 @@ The above is a suggestion. Tailor it to your needs.
|
||||
# Module Loading & Dependencies
|
||||
|
||||
A module is a collection of configuration and run blocks which get applied to the application
|
||||
during the bootstrap process. In its simplest form the module consist of a collection of two kinds
|
||||
during the bootstrap process. In its simplest form the module consists of a collection of two kinds
|
||||
of blocks:
|
||||
|
||||
1. **Configuration blocks** - get executed during the provider registrations and configuration
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
# What are Scopes?
|
||||
|
||||
{@link ng.$rootScope.Scope scope} is an object that refers to the application
|
||||
{@link ng.$rootScope.Scope Scope} is an object that refers to the application
|
||||
model. It is an execution context for {@link expression expressions}. Scopes are
|
||||
arranged in hierarchical structure which mimic the DOM structure of the application. Scopes can
|
||||
watch {@link guide/expression expressions} and propagate events.
|
||||
|
||||
@@ -39,7 +39,7 @@ In general, we recommend against this because it can create unintended XSS vecto
|
||||
|
||||
However, it's ok to mix server-side templating in the bootstrap template (`index.html`) as long
|
||||
as user input cannot be used on the server to output html that would then be processed by Angular
|
||||
in a way that would cause allow for arbitrary code execution.
|
||||
in a way that would allow for arbitrary code execution.
|
||||
|
||||
For instance, you can use server-side templating to dynamically generate CSS, URLs, etc, but not
|
||||
for generating templates that are bootstrapped/compiled by Angular.
|
||||
|
||||
@@ -261,8 +261,10 @@ myModule.filter('length', function() {
|
||||
|
||||
describe('length filter', function() {
|
||||
|
||||
var $filter;
|
||||
|
||||
beforeEach(inject(function(_$filter_){
|
||||
$filter= _$filter_;
|
||||
$filter = _$filter_;
|
||||
}));
|
||||
|
||||
it('returns 0 when given null', function() {
|
||||
|
||||
@@ -194,7 +194,7 @@ Conditionally showing and hiding things using jQuery is a common pattern in othe
|
||||
`ng-show` (and `ng-hide`) conditionally show and hide elements based on boolean expressions.
|
||||
Describe the conditions for showing and hiding an element in terms of `$scope` variables:
|
||||
|
||||
<div ng-show="!loggedIn">Click <a href="#/login">here</a> to log in</div>
|
||||
<div ng-show="!loggedIn"><a href="#/login">Click here to log in</a></div>
|
||||
|
||||
Note also the counterpart `ng-hide` and similar `ng-disabled`.
|
||||
Note especially the powerful `ng-switch` that should be used instead of several mutually exclusive `ng-show`s.
|
||||
|
||||
@@ -129,7 +129,8 @@ Once you have Node.js installed on your machine you can download the tool depend
|
||||
npm install
|
||||
```
|
||||
|
||||
This command will download the following tools, into the `node_modules` directory:
|
||||
This command reads angular-phonecat's `package.json` file and downloads the following tools
|
||||
into the `node_modules` directory:
|
||||
|
||||
- [Bower][bower] - client-side code package manager
|
||||
- [Http-Server][http-server] - simple local static web server
|
||||
@@ -198,7 +199,7 @@ http://localhost:8000/app/index.html
|
||||
|
||||
<div class="alert alert-info">
|
||||
To serve the web app on a different ip address or port, edit the "start" script within package.json.
|
||||
You can `-a` to set the address and `-p` to set the port.
|
||||
You can use `-a` to set the address and `-p` to set the port.
|
||||
</div>
|
||||
|
||||
### Running Unit Tests
|
||||
@@ -270,6 +271,7 @@ It is good to run the end to end tests whenever you make changes to the HTML vie
|
||||
that the application as a whole is executing correctly. It is very common to run End to End tests
|
||||
before pushing a new commit of changes to a remote repository.
|
||||
|
||||
Now that you have set up your local machine, let's get started with the tutorial: {@link step_00 Step 0 - Bootstrapping}
|
||||
|
||||
[git]: http://git-scm.com/
|
||||
[node]: http://nodejs.org/
|
||||
|
||||
@@ -31,7 +31,7 @@ npm install
|
||||
|
||||
To see the app running in a browser, open a *separate* terminal/command line tab or window, then
|
||||
run `npm start` to start the web server. Now, open a browser window for the app and navigate to
|
||||
<a href="http://localhost:8000/app/" target="_blank">`http://localhost:8000/app/`</a>
|
||||
<a href="http://localhost:8000/app/" target="_blank" title="Open app on localhost">`http://localhost:8000/app/`</a>
|
||||
|
||||
Note that if you already ran the master branch app prior to checking out step-0, you may see the cached
|
||||
master version of the app in your browser window at this point. Just hit refresh to re-load the page.
|
||||
@@ -91,22 +91,22 @@ being the element on which the `ngApp` directive was defined.
|
||||
|
||||
Nothing here {{'yet' + '!'}}
|
||||
|
||||
This line demonstrates two core features of Angular's templating capabilities:
|
||||
This line demonstrates two core features of Angular's templating capabilities:
|
||||
|
||||
* a binding, denoted by double-curlies `{{ }}`
|
||||
* a simple expression `'yet' + '!'` used in this binding.
|
||||
* a binding, denoted by double-curlies `{{ }}`
|
||||
* a simple expression `'yet' + '!'` used in this binding.
|
||||
|
||||
The binding tells Angular that it should evaluate an expression and insert the result into the
|
||||
DOM in place of the binding. Rather than a one-time insert, as we'll see in the next steps, a
|
||||
binding will result in efficient continuous updates whenever the result of the expression
|
||||
evaluation changes.
|
||||
The binding tells Angular that it should evaluate an expression and insert the result into the
|
||||
DOM in place of the binding. Rather than a one-time insert, as we'll see in the next steps, a
|
||||
binding will result in efficient continuous updates whenever the result of the expression
|
||||
evaluation changes.
|
||||
|
||||
{@link guide/expression Angular expression} is a JavaScript-like code snippet that is
|
||||
evaluated by Angular in the context of the current model scope, rather than within the scope of
|
||||
the global context (`window`).
|
||||
{@link guide/expression Angular expression} is a JavaScript-like code snippet that is
|
||||
evaluated by Angular in the context of the current model scope, rather than within the scope of
|
||||
the global context (`window`).
|
||||
|
||||
As expected, once this template is processed by Angular, the html page contains the text:
|
||||
"Nothing here yet!".
|
||||
As expected, once this template is processed by Angular, the html page contains the text:
|
||||
"Nothing here yet!".
|
||||
|
||||
## Bootstrapping AngularJS apps
|
||||
|
||||
|
||||
+22
-16
@@ -2385,10 +2385,10 @@
|
||||
}
|
||||
},
|
||||
"dgeni-packages": {
|
||||
"version": "0.10.13",
|
||||
"version": "0.10.17",
|
||||
"dependencies": {
|
||||
"catharsis": {
|
||||
"version": "0.8.6",
|
||||
"version": "0.8.7",
|
||||
"dependencies": {
|
||||
"underscore-contrib": {
|
||||
"version": "0.3.0",
|
||||
@@ -2404,10 +2404,10 @@
|
||||
"version": "2.3.0",
|
||||
"dependencies": {
|
||||
"camel-case": {
|
||||
"version": "1.1.1"
|
||||
"version": "1.1.2"
|
||||
},
|
||||
"constant-case": {
|
||||
"version": "1.1.0"
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"dot-case": {
|
||||
"version": "1.1.1"
|
||||
@@ -2428,7 +2428,7 @@
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"pascal-case": {
|
||||
"version": "1.1.0"
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"path-case": {
|
||||
"version": "1.1.1"
|
||||
@@ -2440,10 +2440,10 @@
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"swap-case": {
|
||||
"version": "1.1.0"
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"title-case": {
|
||||
"version": "1.1.0"
|
||||
"version": "1.1.1"
|
||||
},
|
||||
"upper-case": {
|
||||
"version": "1.1.2"
|
||||
@@ -2468,7 +2468,7 @@
|
||||
}
|
||||
},
|
||||
"htmlparser2": {
|
||||
"version": "3.8.2",
|
||||
"version": "3.8.3",
|
||||
"dependencies": {
|
||||
"domhandler": {
|
||||
"version": "2.3.0"
|
||||
@@ -2518,21 +2518,21 @@
|
||||
"version": "0.3.0",
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "2.5.0"
|
||||
"version": "2.6.4"
|
||||
},
|
||||
"sigmund": {
|
||||
"version": "1.0.0"
|
||||
"version": "1.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nunjucks": {
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.4",
|
||||
"dependencies": {
|
||||
"optimist": {
|
||||
"version": "0.6.1",
|
||||
"dependencies": {
|
||||
"wordwrap": {
|
||||
"version": "0.0.2"
|
||||
"version": "0.0.3"
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.10"
|
||||
@@ -2552,10 +2552,10 @@
|
||||
"version": "0.2.14",
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "2.5.0"
|
||||
"version": "2.6.4"
|
||||
},
|
||||
"sigmund": {
|
||||
"version": "1.0.0"
|
||||
"version": "1.0.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2582,10 +2582,10 @@
|
||||
"version": "0.1.6"
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "0.3.5",
|
||||
"version": "0.3.6",
|
||||
"dependencies": {
|
||||
"nan": {
|
||||
"version": "1.5.3"
|
||||
"version": "1.8.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2621,6 +2621,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "4.3.6"
|
||||
},
|
||||
"shelljs": {
|
||||
"version": "0.5.1"
|
||||
},
|
||||
"winston": {
|
||||
"version": "0.7.3",
|
||||
"dependencies": {
|
||||
|
||||
Generated
+207
-196
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"license": "MIT",
|
||||
"branchVersion": "^1.4.0-beta.0",
|
||||
"branchPattern": "1.4.*",
|
||||
"repository": {
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
"isBlob": false,
|
||||
"isBoolean": false,
|
||||
"isPromiseLike": false,
|
||||
"hasCustomToString": false,
|
||||
"trim": false,
|
||||
"escapeForRegexp": false,
|
||||
"isElement": false,
|
||||
|
||||
+33
-28
@@ -352,8 +352,12 @@ function baseExtend(dst, objs, deep) {
|
||||
var src = obj[key];
|
||||
|
||||
if (deep && isObject(src)) {
|
||||
if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
|
||||
baseExtend(dst[key], [src], true);
|
||||
if (isDate(src)) {
|
||||
dst[key] = new Date(src.valueOf());
|
||||
} else {
|
||||
if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
|
||||
baseExtend(dst[key], [src], true);
|
||||
}
|
||||
} else {
|
||||
dst[key] = src;
|
||||
}
|
||||
@@ -464,6 +468,11 @@ identity.$inject = [];
|
||||
|
||||
function valueFn(value) {return function() {return value;};}
|
||||
|
||||
function hasCustomToString(obj) {
|
||||
return isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc function
|
||||
* @name angular.isUndefined
|
||||
@@ -795,9 +804,18 @@ function copy(source, destination, stackSource, stackDest) {
|
||||
|
||||
if (!destination) {
|
||||
destination = source;
|
||||
if (source) {
|
||||
if (isObject(source)) {
|
||||
var index;
|
||||
if (stackSource && (index = stackSource.indexOf(source)) !== -1) {
|
||||
return stackDest[index];
|
||||
}
|
||||
|
||||
// TypedArray, Date and RegExp have specific copy functionality and must be
|
||||
// pushed onto the stack before returning.
|
||||
// Array and other objects create the base object and recurse to copy child
|
||||
// objects. The array/object will be pushed onto the stack when recursed.
|
||||
if (isArray(source)) {
|
||||
destination = copy(source, [], stackSource, stackDest);
|
||||
return copy(source, [], stackSource, stackDest);
|
||||
} else if (isTypedArray(source)) {
|
||||
destination = new source.constructor(source);
|
||||
} else if (isDate(source)) {
|
||||
@@ -805,9 +823,14 @@ function copy(source, destination, stackSource, stackDest) {
|
||||
} else if (isRegExp(source)) {
|
||||
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
|
||||
destination.lastIndex = source.lastIndex;
|
||||
} else if (isObject(source)) {
|
||||
} else {
|
||||
var emptyObject = Object.create(getPrototypeOf(source));
|
||||
destination = copy(source, emptyObject, stackSource, stackDest);
|
||||
return copy(source, emptyObject, stackSource, stackDest);
|
||||
}
|
||||
|
||||
if (stackDest) {
|
||||
stackSource.push(source);
|
||||
stackDest.push(destination);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -818,9 +841,6 @@ function copy(source, destination, stackSource, stackDest) {
|
||||
stackDest = stackDest || [];
|
||||
|
||||
if (isObject(source)) {
|
||||
var index = stackSource.indexOf(source);
|
||||
if (index !== -1) return stackDest[index];
|
||||
|
||||
stackSource.push(source);
|
||||
stackDest.push(destination);
|
||||
}
|
||||
@@ -829,12 +849,7 @@ function copy(source, destination, stackSource, stackDest) {
|
||||
if (isArray(source)) {
|
||||
destination.length = 0;
|
||||
for (var i = 0; i < source.length; i++) {
|
||||
result = copy(source[i], null, stackSource, stackDest);
|
||||
if (isObject(source[i])) {
|
||||
stackSource.push(source[i]);
|
||||
stackDest.push(result);
|
||||
}
|
||||
destination.push(result);
|
||||
destination.push(copy(source[i], null, stackSource, stackDest));
|
||||
}
|
||||
} else {
|
||||
var h = destination.$$hashKey;
|
||||
@@ -848,20 +863,20 @@ function copy(source, destination, stackSource, stackDest) {
|
||||
if (isBlankObject(source)) {
|
||||
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
|
||||
for (key in source) {
|
||||
putValue(key, source[key], destination, stackSource, stackDest);
|
||||
destination[key] = copy(source[key], null, stackSource, stackDest);
|
||||
}
|
||||
} else if (source && typeof source.hasOwnProperty === 'function') {
|
||||
// Slow path, which must rely on hasOwnProperty
|
||||
for (key in source) {
|
||||
if (source.hasOwnProperty(key)) {
|
||||
putValue(key, source[key], destination, stackSource, stackDest);
|
||||
destination[key] = copy(source[key], null, stackSource, stackDest);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Slowest path --- hasOwnProperty can't be called as a method
|
||||
for (key in source) {
|
||||
if (hasOwnProperty.call(source, key)) {
|
||||
putValue(key, source[key], destination, stackSource, stackDest);
|
||||
destination[key] = copy(source[key], null, stackSource, stackDest);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -869,16 +884,6 @@ function copy(source, destination, stackSource, stackDest) {
|
||||
}
|
||||
}
|
||||
return destination;
|
||||
|
||||
function putValue(key, val, destination, stackSource, stackDest) {
|
||||
// No context allocation, trivial outer scope, easily inlined
|
||||
var result = copy(val, null, stackSource, stackDest);
|
||||
if (isObject(val)) {
|
||||
stackSource.push(val);
|
||||
stackDest.push(result);
|
||||
}
|
||||
destination[key] = result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -645,7 +645,7 @@ function createInjector(modulesToLoad, strictDi) {
|
||||
}));
|
||||
|
||||
|
||||
forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
|
||||
forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
|
||||
|
||||
return instanceInjector;
|
||||
|
||||
|
||||
+9
-1
@@ -182,6 +182,13 @@ function jqLiteAcceptsData(node) {
|
||||
return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
|
||||
}
|
||||
|
||||
function jqLiteHasData(node) {
|
||||
for (var key in jqCache[node.ng339]) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function jqLiteBuildFragment(html, context) {
|
||||
var tmp, tag, wrap,
|
||||
fragment = context.createDocumentFragment(),
|
||||
@@ -556,7 +563,8 @@ function getAliasedAttrName(element, name) {
|
||||
|
||||
forEach({
|
||||
data: jqLiteData,
|
||||
removeData: jqLiteRemoveData
|
||||
removeData: jqLiteRemoveData,
|
||||
hasData: jqLiteHasData
|
||||
}, function(fn, name) {
|
||||
JQLite[name] = fn;
|
||||
});
|
||||
|
||||
+21
-8
@@ -146,7 +146,7 @@ function setupModuleLoader(window) {
|
||||
* @description
|
||||
* See {@link auto.$provide#provider $provide.provider()}.
|
||||
*/
|
||||
provider: invokeLater('$provide', 'provider'),
|
||||
provider: invokeLaterAndSetModuleName('$provide', 'provider'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
@@ -157,7 +157,7 @@ function setupModuleLoader(window) {
|
||||
* @description
|
||||
* See {@link auto.$provide#factory $provide.factory()}.
|
||||
*/
|
||||
factory: invokeLater('$provide', 'factory'),
|
||||
factory: invokeLaterAndSetModuleName('$provide', 'factory'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
@@ -168,7 +168,7 @@ function setupModuleLoader(window) {
|
||||
* @description
|
||||
* See {@link auto.$provide#service $provide.service()}.
|
||||
*/
|
||||
service: invokeLater('$provide', 'service'),
|
||||
service: invokeLaterAndSetModuleName('$provide', 'service'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
@@ -203,7 +203,7 @@ function setupModuleLoader(window) {
|
||||
* @description
|
||||
* See {@link auto.$provide#decorator $provide.decorator()}.
|
||||
*/
|
||||
decorator: invokeLater('$provide', 'decorator'),
|
||||
decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
@@ -237,7 +237,7 @@ function setupModuleLoader(window) {
|
||||
* See {@link ng.$animateProvider#register $animateProvider.register()} and
|
||||
* {@link ngAnimate ngAnimate module} for more information.
|
||||
*/
|
||||
animation: invokeLater('$animateProvider', 'register'),
|
||||
animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
@@ -255,7 +255,7 @@ function setupModuleLoader(window) {
|
||||
* (`myapp_subsection_filterx`).
|
||||
* </div>
|
||||
*/
|
||||
filter: invokeLater('$filterProvider', 'register'),
|
||||
filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
@@ -267,7 +267,7 @@ function setupModuleLoader(window) {
|
||||
* @description
|
||||
* See {@link ng.$controllerProvider#register $controllerProvider.register()}.
|
||||
*/
|
||||
controller: invokeLater('$controllerProvider', 'register'),
|
||||
controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
@@ -280,7 +280,7 @@ function setupModuleLoader(window) {
|
||||
* @description
|
||||
* See {@link ng.$compileProvider#directive $compileProvider.directive()}.
|
||||
*/
|
||||
directive: invokeLater('$compileProvider', 'directive'),
|
||||
directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
@@ -330,6 +330,19 @@ function setupModuleLoader(window) {
|
||||
return moduleInstance;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} provider
|
||||
* @param {string} method
|
||||
* @returns {angular.Module}
|
||||
*/
|
||||
function invokeLaterAndSetModuleName(provider, method) {
|
||||
return function(recipeName, factoryFunction) {
|
||||
if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
|
||||
invokeQueue.push([provider, method, arguments]);
|
||||
return moduleInstance;
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
+2
-2
@@ -63,7 +63,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
|
||||
function getHash(url) {
|
||||
var index = url.indexOf('#');
|
||||
return index === -1 ? '' : url.substr(index + 1);
|
||||
return index === -1 ? '' : url.substr(index);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,7 +147,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
// Do the assignment again so that those two variables are referentially identical.
|
||||
lastHistoryState = cachedState;
|
||||
} else {
|
||||
if (!sameBase) {
|
||||
if (!sameBase || reloadLocation) {
|
||||
reloadLocation = url;
|
||||
}
|
||||
if (replace) {
|
||||
|
||||
+63
-33
@@ -399,13 +399,16 @@
|
||||
* * `controller` - the directive's required controller instance(s) - Instances are shared
|
||||
* among all directives, which allows the directives to use the controllers as a communication
|
||||
* channel. The exact value depends on the directive's `require` property:
|
||||
* * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
|
||||
* * `string`: the controller instance
|
||||
* * `array`: array of controller instances
|
||||
* * no controller(s) required: `undefined`
|
||||
*
|
||||
* If a required controller cannot be found, and it is optional, the instance is `null`,
|
||||
* otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
|
||||
*
|
||||
* Note that you can also require the directive's own controller - it will be made available like
|
||||
* like 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.
|
||||
@@ -852,6 +855,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (isObject(bindings.isolateScope)) {
|
||||
directive.$$isolateBindings = bindings.isolateScope;
|
||||
}
|
||||
directive.$$moduleName = directiveFactory.$$moduleName;
|
||||
directives.push(directive);
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
@@ -1423,8 +1427,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
if (nodeLinkFn.transcludeOnThisElement) {
|
||||
childBoundTranscludeFn = createBoundTranscludeFn(
|
||||
scope, nodeLinkFn.transclude, parentBoundTranscludeFn,
|
||||
nodeLinkFn.elementTranscludeOnThisElement);
|
||||
scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
|
||||
|
||||
} else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
|
||||
childBoundTranscludeFn = parentBoundTranscludeFn;
|
||||
@@ -1446,7 +1449,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
}
|
||||
|
||||
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) {
|
||||
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
|
||||
|
||||
var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
|
||||
|
||||
@@ -1545,6 +1548,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
break;
|
||||
case NODE_TYPE_TEXT: /* Text Node */
|
||||
if (msie === 11) {
|
||||
// Workaround for #11781
|
||||
while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
|
||||
node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
|
||||
node.parentNode.removeChild(node.nextSibling);
|
||||
}
|
||||
}
|
||||
addTextInterpolateDirective(directives, node.nodeValue);
|
||||
break;
|
||||
case NODE_TYPE_COMMENT: /* Comment */
|
||||
@@ -1644,7 +1654,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
previousCompileContext = previousCompileContext || {};
|
||||
|
||||
var terminalPriority = -Number.MAX_VALUE,
|
||||
newScopeDirective,
|
||||
newScopeDirective = previousCompileContext.newScopeDirective,
|
||||
controllerDirectives = previousCompileContext.controllerDirectives,
|
||||
newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
|
||||
templateDirective = previousCompileContext.templateDirective,
|
||||
@@ -1810,6 +1820,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
|
||||
templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
|
||||
controllerDirectives: controllerDirectives,
|
||||
newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
|
||||
newIsolateScopeDirective: newIsolateScopeDirective,
|
||||
templateDirective: templateDirective,
|
||||
nonTlbTranscludeDirective: nonTlbTranscludeDirective
|
||||
@@ -1837,7 +1848,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
|
||||
nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
|
||||
nodeLinkFn.elementTranscludeOnThisElement = hasElementTranscludeDirective;
|
||||
nodeLinkFn.templateOnThisElement = hasTemplate;
|
||||
nodeLinkFn.transclude = childTranscludeFn;
|
||||
|
||||
@@ -1998,9 +2008,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
for (i in elementControllers) {
|
||||
controller = elementControllers[i];
|
||||
var controllerResult = controller();
|
||||
|
||||
if (controllerResult !== controller.instance) {
|
||||
// If the controller constructor has a return value, overwrite the instance
|
||||
// from setupControllers and update the element data
|
||||
controller.instance = controllerResult;
|
||||
$element.data('$' + directive.name + 'Controller', controllerResult);
|
||||
$element.data('$' + i + 'Controller', controllerResult);
|
||||
if (controller === controllerForBindings) {
|
||||
// Remove and re-install bindToController bindings
|
||||
thisLinkFn.$$destroyBindings();
|
||||
@@ -2192,7 +2205,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
$compileNode.empty();
|
||||
|
||||
$templateRequest($sce.getTrustedResourceUrl(templateUrl))
|
||||
$templateRequest(templateUrl)
|
||||
.then(function(content) {
|
||||
var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
|
||||
|
||||
@@ -2300,11 +2313,18 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
return a.index - b.index;
|
||||
}
|
||||
|
||||
|
||||
function assertNoDuplicate(what, previousDirective, directive, element) {
|
||||
|
||||
function wrapModuleNameIfDefined(moduleName) {
|
||||
return moduleName ?
|
||||
(' (module: ' + moduleName + ')') :
|
||||
'';
|
||||
}
|
||||
|
||||
if (previousDirective) {
|
||||
throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}',
|
||||
previousDirective.name, directive.name, what, startingTag(element));
|
||||
throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
|
||||
previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
|
||||
directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2485,26 +2505,28 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
var fragment = document.createDocumentFragment();
|
||||
fragment.appendChild(firstElementToRemove);
|
||||
|
||||
// Copy over user data (that includes Angular's $scope etc.). Don't copy private
|
||||
// data here because there's no public interface in jQuery to do that and copying over
|
||||
// event listeners (which is the main use of private data) wouldn't work anyway.
|
||||
jqLite(newNode).data(jqLite(firstElementToRemove).data());
|
||||
if (jqLite.hasData(firstElementToRemove)) {
|
||||
// Copy over user data (that includes Angular's $scope etc.). Don't copy private
|
||||
// data here because there's no public interface in jQuery to do that and copying over
|
||||
// event listeners (which is the main use of private data) wouldn't work anyway.
|
||||
jqLite(newNode).data(jqLite(firstElementToRemove).data());
|
||||
|
||||
// Remove data of the replaced element. We cannot just call .remove()
|
||||
// on the element it since that would deallocate scope that is needed
|
||||
// for the new node. Instead, remove the data "manually".
|
||||
if (!jQuery) {
|
||||
delete jqLite.cache[firstElementToRemove[jqLite.expando]];
|
||||
} else {
|
||||
// jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
|
||||
// the replaced element. The cleanData version monkey-patched by Angular would cause
|
||||
// the scope to be trashed and we do need the very same scope to work with the new
|
||||
// element. However, we cannot just cache the non-patched version and use it here as
|
||||
// that would break if another library patches the method after Angular does (one
|
||||
// example is jQuery UI). Instead, set a flag indicating scope destroying should be
|
||||
// skipped this one time.
|
||||
skipDestroyOnNextJQueryCleanData = true;
|
||||
jQuery.cleanData([firstElementToRemove]);
|
||||
// Remove data of the replaced element. We cannot just call .remove()
|
||||
// on the element it since that would deallocate scope that is needed
|
||||
// for the new node. Instead, remove the data "manually".
|
||||
if (!jQuery) {
|
||||
delete jqLite.cache[firstElementToRemove[jqLite.expando]];
|
||||
} else {
|
||||
// jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
|
||||
// the replaced element. The cleanData version monkey-patched by Angular would cause
|
||||
// the scope to be trashed and we do need the very same scope to work with the new
|
||||
// element. However, we cannot just cache the non-patched version and use it here as
|
||||
// that would break if another library patches the method after Angular does (one
|
||||
// example is jQuery UI). Instead, set a flag indicating scope destroying should be
|
||||
// skipped this one time.
|
||||
skipDestroyOnNextJQueryCleanData = true;
|
||||
jQuery.cleanData([firstElementToRemove]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
|
||||
@@ -2545,9 +2567,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
lastValue,
|
||||
parentGet, parentSet, compare;
|
||||
|
||||
if (!hasOwnProperty.call(attrs, attrName)) {
|
||||
// In the case of user defined a binding with the same name as a method in Object.prototype but didn't set
|
||||
// the corresponding attribute. We need to make sure subsequent code won't access to the prototype function
|
||||
attrs[attrName] = undefined;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
|
||||
case '@':
|
||||
if (!attrs[attrName] && !optional) {
|
||||
destination[scopeName] = undefined;
|
||||
}
|
||||
|
||||
attrs.$observe(attrName, function(value) {
|
||||
destination[scopeName] = value;
|
||||
});
|
||||
@@ -2564,6 +2596,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
return;
|
||||
}
|
||||
parentGet = $parse(attrs[attrName]);
|
||||
|
||||
if (parentGet.literal) {
|
||||
compare = equals;
|
||||
} else {
|
||||
@@ -2602,9 +2635,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
break;
|
||||
|
||||
case '&':
|
||||
// Don't assign Object.prototype method to scope
|
||||
if (!attrs.hasOwnProperty(attrName) && optional) break;
|
||||
|
||||
parentGet = $parse(attrs[attrName]);
|
||||
|
||||
// Don't assign noop to destination if expression is not valid
|
||||
|
||||
@@ -206,6 +206,13 @@
|
||||
* @priority 100
|
||||
*
|
||||
* @description
|
||||
* Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
|
||||
*
|
||||
* Note that this directive should not be used together with {@link ngModel `ngModel`},
|
||||
* as this can lead to unexpected behavior.
|
||||
*
|
||||
* ### Why do we need `ngChecked`?
|
||||
*
|
||||
* The HTML specification does not require browsers to preserve the values of boolean attributes
|
||||
* such as checked. (Their presence means true and their absence means false.)
|
||||
* If we put an Angular interpolation expression into such an attribute then the
|
||||
@@ -230,7 +237,7 @@
|
||||
*
|
||||
* @element INPUT
|
||||
* @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
|
||||
* then special attribute "checked" will be set on the element
|
||||
* then the `checked` attribute will be set on the element
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
|
||||
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
|
||||
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
|
||||
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
|
||||
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
|
||||
var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
|
||||
var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
|
||||
var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
|
||||
@@ -612,6 +612,16 @@ var inputType = {
|
||||
* error docs for more information and an example of how to convert your model if necessary.
|
||||
* </div>
|
||||
*
|
||||
* ## Issues with HTML5 constraint validation
|
||||
*
|
||||
* In browsers that follow the
|
||||
* [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
|
||||
* `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
|
||||
* If a non-number is entered in the input, the browser will report the value as an empty string,
|
||||
* which means the view / model values in `ngModel` and subsequently the scope value
|
||||
* will also be an empty string.
|
||||
*
|
||||
*
|
||||
* @param {string} ngModel Assignable angular expression to data-bind to.
|
||||
* @param {string=} name Property name of the form under which the control is published.
|
||||
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
|
||||
@@ -903,12 +913,15 @@ var inputType = {
|
||||
* HTML radio button.
|
||||
*
|
||||
* @param {string} ngModel Assignable angular expression to data-bind to.
|
||||
* @param {string} value The value to which the expression should be set when selected.
|
||||
* @param {string} value The value to which the `ngModel` expression should be set when selected.
|
||||
* Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
|
||||
* too. Use `ngValue` if you need complex models (`number`, `object`, ...).
|
||||
* @param {string=} name Property name of the form under which the control is published.
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
* @param {string} ngValue Angular expression which sets the value to which the expression should
|
||||
* be set when selected.
|
||||
* @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
|
||||
* is selected. Should be used instead of the `value` attribute if you need
|
||||
* a non-string `ngModel` (`boolean`, `array`, ...).
|
||||
*
|
||||
* @example
|
||||
<example name="radio-input-directive" module="radioExample">
|
||||
|
||||
@@ -162,7 +162,7 @@ function classDirective(name, selector) {
|
||||
* @example Example that demonstrates basic bindings via ngClass directive.
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p>
|
||||
<p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
|
||||
<label>
|
||||
<input type="checkbox" ng-model="deleted">
|
||||
deleted (apply "strike" class)
|
||||
@@ -173,7 +173,7 @@ function classDirective(name, selector) {
|
||||
</label><br>
|
||||
<label>
|
||||
<input type="checkbox" ng-model="error">
|
||||
error (apply "red" class)
|
||||
error (apply "has-error" class)
|
||||
</label>
|
||||
<hr>
|
||||
<p ng-class="style">Using String Syntax</p>
|
||||
@@ -202,6 +202,10 @@ function classDirective(name, selector) {
|
||||
.red {
|
||||
color: red;
|
||||
}
|
||||
.has-error {
|
||||
color: red;
|
||||
background-color: yellow;
|
||||
}
|
||||
.orange {
|
||||
color: orange;
|
||||
}
|
||||
@@ -212,13 +216,13 @@ function classDirective(name, selector) {
|
||||
it('should let you toggle the class', function() {
|
||||
|
||||
expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
|
||||
expect(ps.first().getAttribute('class')).not.toMatch(/red/);
|
||||
expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
|
||||
|
||||
element(by.model('important')).click();
|
||||
expect(ps.first().getAttribute('class')).toMatch(/bold/);
|
||||
|
||||
element(by.model('error')).click();
|
||||
expect(ps.first().getAttribute('class')).toMatch(/red/);
|
||||
expect(ps.first().getAttribute('class')).toMatch(/has-error/);
|
||||
});
|
||||
|
||||
it('should let you toggle string example', function() {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* @ngdoc directive
|
||||
* @name ngIf
|
||||
* @restrict A
|
||||
* @multiElement
|
||||
*
|
||||
* @description
|
||||
* The `ngIf` directive removes or recreates a portion of the DOM tree based on an
|
||||
|
||||
@@ -178,8 +178,8 @@
|
||||
* @param {Object} angularEvent Synthetic event object.
|
||||
* @param {String} src URL of content to load.
|
||||
*/
|
||||
var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate', '$sce',
|
||||
function($templateRequest, $anchorScroll, $animate, $sce) {
|
||||
var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
|
||||
function($templateRequest, $anchorScroll, $animate) {
|
||||
return {
|
||||
restrict: 'ECA',
|
||||
priority: 400,
|
||||
@@ -215,7 +215,7 @@ var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate', '$sce
|
||||
}
|
||||
};
|
||||
|
||||
scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
|
||||
scope.$watch(srcExp, function ngIncludeWatchAction(src) {
|
||||
var afterAnimation = function() {
|
||||
if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
|
||||
$anchorScroll();
|
||||
|
||||
@@ -1101,7 +1101,7 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
|
||||
* - `debounce`: integer value which contains the debounce model update value in milliseconds. A
|
||||
* value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
|
||||
* custom value for each event. For example:
|
||||
* `ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"`
|
||||
* `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
|
||||
* - `allowInvalid`: boolean value which indicates that the model can be set with values that did
|
||||
* not validate correctly instead of the default behavior of setting the model to undefined.
|
||||
* - `getterSetter`: boolean value which determines whether or not to treat functions bound to
|
||||
@@ -1351,7 +1351,9 @@ function addSetValidityMethod(context) {
|
||||
function isObjectEmpty(obj) {
|
||||
if (obj) {
|
||||
for (var prop in obj) {
|
||||
return false;
|
||||
if (obj.hasOwnProperty(prop)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -292,19 +292,41 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
this.disabled = disabled;
|
||||
}
|
||||
|
||||
function getOptionValuesKeys(optionValues) {
|
||||
var optionValuesKeys;
|
||||
|
||||
if (!keyName && isArrayLike(optionValues)) {
|
||||
optionValuesKeys = optionValues;
|
||||
} else {
|
||||
// if object, extract keys, in enumeration order, unsorted
|
||||
optionValuesKeys = [];
|
||||
for (var itemKey in optionValues) {
|
||||
if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
|
||||
optionValuesKeys.push(itemKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
return optionValuesKeys;
|
||||
}
|
||||
|
||||
return {
|
||||
trackBy: trackBy,
|
||||
getTrackByValue: getTrackByValue,
|
||||
getWatchables: $parse(valuesFn, function(values) {
|
||||
getWatchables: $parse(valuesFn, function(optionValues) {
|
||||
// Create a collection of things that we would like to watch (watchedArray)
|
||||
// so that they can all be watched using a single $watchCollection
|
||||
// that only runs the handler once if anything changes
|
||||
var watchedArray = [];
|
||||
values = values || [];
|
||||
optionValues = optionValues || [];
|
||||
|
||||
Object.keys(values).forEach(function getWatchable(key) {
|
||||
var locals = getLocals(values[key], key);
|
||||
var selectValue = getTrackByValueFn(values[key], locals);
|
||||
var optionValuesKeys = getOptionValuesKeys(optionValues);
|
||||
var optionValuesLength = optionValuesKeys.length;
|
||||
for (var index = 0; index < optionValuesLength; index++) {
|
||||
var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
|
||||
var value = optionValues[key];
|
||||
|
||||
var locals = getLocals(optionValues[key], key);
|
||||
var selectValue = getTrackByValueFn(optionValues[key], locals);
|
||||
watchedArray.push(selectValue);
|
||||
|
||||
// Only need to watch the displayFn if there is a specific label expression
|
||||
@@ -318,7 +340,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
var disableWhen = disableWhenFn(scope, locals);
|
||||
watchedArray.push(disableWhen);
|
||||
}
|
||||
});
|
||||
}
|
||||
return watchedArray;
|
||||
}),
|
||||
|
||||
@@ -330,21 +352,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
// The option values were already computed in the `getWatchables` fn,
|
||||
// which must have been called to trigger `getOptions`
|
||||
var optionValues = valuesFn(scope) || [];
|
||||
var optionValuesKeys;
|
||||
|
||||
|
||||
if (!keyName && isArrayLike(optionValues)) {
|
||||
optionValuesKeys = optionValues;
|
||||
} else {
|
||||
// if object, extract keys, in enumeration order, unsorted
|
||||
optionValuesKeys = [];
|
||||
for (var itemKey in optionValues) {
|
||||
if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
|
||||
optionValuesKeys.push(itemKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var optionValuesKeys = getOptionValuesKeys(optionValues);
|
||||
var optionValuesLength = optionValuesKeys.length;
|
||||
|
||||
for (var index = 0; index < optionValuesLength; index++) {
|
||||
@@ -706,8 +714,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
// Check to see if the value has changed due to the update to the options
|
||||
if (!ngModelCtrl.$isEmpty(previousValue)) {
|
||||
var nextValue = selectCtrl.readValue();
|
||||
if (ngOptions.trackBy && !equals(previousValue, nextValue) ||
|
||||
previousValue !== nextValue) {
|
||||
if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
|
||||
ngModelCtrl.$setViewValue(nextValue);
|
||||
ngModelCtrl.$render();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngRepeat
|
||||
* @multiElement
|
||||
*
|
||||
* @description
|
||||
* The `ngRepeat` directive instantiates a template once per item from a collection. Each template
|
||||
@@ -97,6 +98,15 @@
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** `track by` must always be the last expression:
|
||||
* </div>
|
||||
* ```
|
||||
* <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
|
||||
* {{model.name}}
|
||||
* </div>
|
||||
* ```
|
||||
*
|
||||
* # Special repeat start and end points
|
||||
* To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
|
||||
* the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
|
||||
@@ -168,8 +178,9 @@
|
||||
* which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
|
||||
* is specified, ng-repeat associates elements by identity. It is an error to have
|
||||
* more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
|
||||
* mapped to the same DOM element, which is not possible.) If filters are used in the expression, they should be
|
||||
* applied before the tracking expression.
|
||||
* mapped to the same DOM element, which is not possible.)
|
||||
*
|
||||
* Note that the tracking expression must come last, after any filters, and the alias expression.
|
||||
*
|
||||
* For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
|
||||
* will be associated by item identity in the array.
|
||||
|
||||
@@ -5,6 +5,7 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngShow
|
||||
* @multiElement
|
||||
*
|
||||
* @description
|
||||
* The `ngShow` directive shows or hides the given HTML element based on the expression
|
||||
@@ -180,6 +181,7 @@ var ngShowDirective = ['$animate', function($animate) {
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name ngHide
|
||||
* @multiElement
|
||||
*
|
||||
* @description
|
||||
* The `ngHide` directive shows or hides the given HTML element based on the expression
|
||||
|
||||
@@ -34,9 +34,11 @@
|
||||
* `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
|
||||
* **will** be matched by `{$: 'John'}`.
|
||||
*
|
||||
* - `function(value, index)`: A predicate function can be used to write arbitrary filters. The
|
||||
* function is called for each element of `array`. The final result is an array of those
|
||||
* elements that the predicate returned true for.
|
||||
* - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
|
||||
* The function is called for each element of the array, with the element, its index, and
|
||||
* the entire array itself as arguments.
|
||||
*
|
||||
* The final result is an array of those elements that the predicate returned true for.
|
||||
*
|
||||
* @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
|
||||
* determining if the expected value (from the filter expression) and actual value (from
|
||||
@@ -161,10 +163,6 @@ function filterFilter() {
|
||||
};
|
||||
}
|
||||
|
||||
function hasCustomToString(obj) {
|
||||
return isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
|
||||
}
|
||||
|
||||
// Helper functions for `filterFilter`
|
||||
function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
|
||||
var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
|
||||
|
||||
@@ -80,9 +80,10 @@ function currencyFilter($locale) {
|
||||
* @description
|
||||
* Formats a number as text.
|
||||
*
|
||||
* If the input is null or undefined, it will just be returned.
|
||||
* If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
|
||||
* If the input is not a number an empty string is returned.
|
||||
*
|
||||
* If the input is an infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
|
||||
*
|
||||
* @param {number|string} number Number to format.
|
||||
* @param {(number|string)=} fractionSize Number of decimal places to round the number to.
|
||||
|
||||
+129
-82
@@ -8,7 +8,7 @@
|
||||
* @description
|
||||
* Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
|
||||
* for strings and numerically for numbers. Note: if you notice numbers are not being sorted
|
||||
* correctly, make sure they are actually being saved as numbers and not strings.
|
||||
* as expected, make sure they are actually being saved as numbers and not strings.
|
||||
*
|
||||
* @param {Array} array The array to sort.
|
||||
* @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
|
||||
@@ -83,19 +83,40 @@
|
||||
{name:'Mike', phone:'555-4321', age:21},
|
||||
{name:'Adam', phone:'555-5678', age:35},
|
||||
{name:'Julie', phone:'555-8765', age:29}];
|
||||
$scope.predicate = '-age';
|
||||
$scope.predicate = 'age';
|
||||
$scope.reverse = true;
|
||||
$scope.order = function(predicate) {
|
||||
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
|
||||
$scope.predicate = predicate;
|
||||
};
|
||||
}]);
|
||||
</script>
|
||||
<style type="text/css">
|
||||
.sortorder:after {
|
||||
content: '\25b2';
|
||||
}
|
||||
.sortorder.reverse:after {
|
||||
content: '\25bc';
|
||||
}
|
||||
</style>
|
||||
<div ng-controller="ExampleController">
|
||||
<pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
|
||||
<hr/>
|
||||
[ <a href="" ng-click="predicate=''">unsorted</a> ]
|
||||
<table class="friend">
|
||||
<tr>
|
||||
<th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a>
|
||||
(<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th>
|
||||
<th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
|
||||
<th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
|
||||
<th>
|
||||
<a href="" ng-click="order('name')">Name</a>
|
||||
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('phone')">Phone Number</a>
|
||||
<span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
<th>
|
||||
<a href="" ng-click="order('age')">Age</a>
|
||||
<span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
|
||||
</th>
|
||||
</tr>
|
||||
<tr ng-repeat="friend in friends | orderBy:predicate:reverse">
|
||||
<td>{{friend.name}}</td>
|
||||
@@ -155,88 +176,114 @@
|
||||
orderByFilter.$inject = ['$parse'];
|
||||
function orderByFilter($parse) {
|
||||
return function(array, sortPredicate, reverseOrder) {
|
||||
|
||||
if (!(isArrayLike(array))) return array;
|
||||
sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate];
|
||||
|
||||
if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
|
||||
if (sortPredicate.length === 0) { sortPredicate = ['+']; }
|
||||
sortPredicate = sortPredicate.map(function(predicate) {
|
||||
var descending = false, get = predicate || identity;
|
||||
if (isString(predicate)) {
|
||||
if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
|
||||
descending = predicate.charAt(0) == '-';
|
||||
predicate = predicate.substring(1);
|
||||
}
|
||||
if (predicate === '') {
|
||||
// Effectively no predicate was passed so we compare identity
|
||||
return reverseComparator(compare, descending);
|
||||
}
|
||||
get = $parse(predicate);
|
||||
if (get.constant) {
|
||||
var key = get();
|
||||
return reverseComparator(function(a, b) {
|
||||
return compare(a[key], b[key]);
|
||||
}, descending);
|
||||
}
|
||||
}
|
||||
return reverseComparator(function(a, b) {
|
||||
return compare(get(a),get(b));
|
||||
}, descending);
|
||||
});
|
||||
return slice.call(array).sort(reverseComparator(comparator, reverseOrder));
|
||||
|
||||
function comparator(o1, o2) {
|
||||
for (var i = 0; i < sortPredicate.length; i++) {
|
||||
var comp = sortPredicate[i](o1, o2);
|
||||
if (comp !== 0) return comp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
function reverseComparator(comp, descending) {
|
||||
return descending
|
||||
? function(a, b) {return comp(b,a);}
|
||||
: comp;
|
||||
var predicates = processPredicates(sortPredicate, reverseOrder);
|
||||
|
||||
// The next three lines are a version of a Swartzian Transform idiom from Perl
|
||||
// (sometimes called the Decorate-Sort-Undecorate idiom)
|
||||
// See https://en.wikipedia.org/wiki/Schwartzian_transform
|
||||
var compareValues = Array.prototype.map.call(array, getComparisonObject);
|
||||
compareValues.sort(doComparison);
|
||||
array = compareValues.map(function(item) { return item.value; });
|
||||
|
||||
return array;
|
||||
|
||||
function getComparisonObject(value, index) {
|
||||
return {
|
||||
value: value,
|
||||
predicateValues: predicates.map(function(predicate) {
|
||||
return getPredicateValue(predicate.get(value), index);
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
function isPrimitive(value) {
|
||||
switch (typeof value) {
|
||||
case 'number': /* falls through */
|
||||
case 'boolean': /* falls through */
|
||||
case 'string':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function objectToString(value) {
|
||||
if (value === null) return 'null';
|
||||
if (typeof value.valueOf === 'function') {
|
||||
value = value.valueOf();
|
||||
if (isPrimitive(value)) return value;
|
||||
}
|
||||
if (typeof value.toString === 'function') {
|
||||
value = value.toString();
|
||||
if (isPrimitive(value)) return value;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function compare(v1, v2) {
|
||||
var t1 = typeof v1;
|
||||
var t2 = typeof v2;
|
||||
if (t1 === t2 && t1 === "object") {
|
||||
v1 = objectToString(v1);
|
||||
v2 = objectToString(v2);
|
||||
}
|
||||
if (t1 === t2) {
|
||||
if (t1 === "string") {
|
||||
v1 = v1.toLowerCase();
|
||||
v2 = v2.toLowerCase();
|
||||
}
|
||||
if (v1 === v2) return 0;
|
||||
return v1 < v2 ? -1 : 1;
|
||||
} else {
|
||||
return t1 < t2 ? -1 : 1;
|
||||
function doComparison(v1, v2) {
|
||||
var result = 0;
|
||||
for (var index=0, length = predicates.length; index < length; ++index) {
|
||||
result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
|
||||
if (result) break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
function processPredicates(sortPredicate, reverseOrder) {
|
||||
reverseOrder = reverseOrder ? -1 : 1;
|
||||
return sortPredicate.map(function(predicate) {
|
||||
var descending = 1, get = identity;
|
||||
|
||||
if (isFunction(predicate)) {
|
||||
get = predicate;
|
||||
} else if (isString(predicate)) {
|
||||
if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
|
||||
descending = predicate.charAt(0) == '-' ? -1 : 1;
|
||||
predicate = predicate.substring(1);
|
||||
}
|
||||
if (predicate !== '') {
|
||||
get = $parse(predicate);
|
||||
if (get.constant) {
|
||||
var key = get();
|
||||
get = function(value) { return value[key]; };
|
||||
}
|
||||
}
|
||||
}
|
||||
return { get: get, descending: descending * reverseOrder };
|
||||
});
|
||||
}
|
||||
|
||||
function isPrimitive(value) {
|
||||
switch (typeof value) {
|
||||
case 'number': /* falls through */
|
||||
case 'boolean': /* falls through */
|
||||
case 'string':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function objectValue(value, index) {
|
||||
// If `valueOf` is a valid function use that
|
||||
if (typeof value.valueOf === 'function') {
|
||||
value = value.valueOf();
|
||||
if (isPrimitive(value)) return value;
|
||||
}
|
||||
// If `toString` is a valid function and not the one from `Object.prototype` use that
|
||||
if (hasCustomToString(value)) {
|
||||
value = value.toString();
|
||||
if (isPrimitive(value)) return value;
|
||||
}
|
||||
// We have a basic object so we use the position of the object in the collection
|
||||
return index;
|
||||
}
|
||||
|
||||
function getPredicateValue(value, index) {
|
||||
var type = typeof value;
|
||||
if (value === null) {
|
||||
type = 'string';
|
||||
value = 'null';
|
||||
} else if (type === 'string') {
|
||||
value = value.toLowerCase();
|
||||
} else if (type === 'object') {
|
||||
value = objectValue(value, index);
|
||||
}
|
||||
return { value: value, type: type };
|
||||
}
|
||||
|
||||
function compare(v1, v2) {
|
||||
var result = 0;
|
||||
if (v1.type === v2.type) {
|
||||
if (v1.value !== v2.value) {
|
||||
result = v1.value < v2.value ? -1 : 1;
|
||||
}
|
||||
} else {
|
||||
result = v1.type < v2.type ? -1 : 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+62
-15
@@ -23,13 +23,17 @@ function $HttpParamSerializerProvider() {
|
||||
* @name $httpParamSerializer
|
||||
* @description
|
||||
*
|
||||
* Default $http params serializer that converts objects to a part of a request URL
|
||||
* Default {@link $http `$http`} params serializer that converts objects to strings
|
||||
* according to the following rules:
|
||||
*
|
||||
* * `{'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)
|
||||
*
|
||||
* Note that serializer will sort the request parameters alphabetically.
|
||||
* */
|
||||
|
||||
this.$get = function() {
|
||||
return function ngParamSerializer(params) {
|
||||
if (!params) return '';
|
||||
@@ -56,7 +60,43 @@ function $HttpParamSerializerJQLikeProvider() {
|
||||
* @name $httpParamSerializerJQLike
|
||||
* @description
|
||||
*
|
||||
* Alternative $http params serializer that follows jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
|
||||
* Alternative {@link $http `$http`} params serializer that follows
|
||||
* jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
|
||||
* The serializer will also sort the params alphabetically.
|
||||
*
|
||||
* To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
|
||||
*
|
||||
* ```js
|
||||
* $http({
|
||||
* url: myUrl,
|
||||
* method: 'GET',
|
||||
* params: myParams,
|
||||
* paramSerializer: '$httpParamSerializerJQLike'
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* It is also possible to set it as the default `paramSerializer` in the
|
||||
* {@link $httpProvider#defaults `$httpProvider`}.
|
||||
*
|
||||
* Additionally, you can inject the serializer and use it explicitly, for example to serialize
|
||||
* form data for submission:
|
||||
*
|
||||
* ```js
|
||||
* .controller(function($http, $httpParamSerializerJQLike) {
|
||||
* //...
|
||||
*
|
||||
* $http({
|
||||
* url: myUrl,
|
||||
* method: 'POST',
|
||||
* data: $httpParamSerializerJQLike(myData),
|
||||
* headers: {
|
||||
* 'Content-Type': 'application/x-www-form-urlencoded'
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* */
|
||||
this.$get = function() {
|
||||
return function jQueryLikeParamSerializer(params) {
|
||||
@@ -213,7 +253,7 @@ function $HttpProvider() {
|
||||
*
|
||||
* - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
|
||||
* that will provide the cache for all requests who set their `cache` property to `true`.
|
||||
* If you set the `default.cache = false` then only requests that specify their own custom
|
||||
* If you set the `defaults.cache = false` then only requests that specify their own custom
|
||||
* cache object will be cached. See {@link $http#caching $http Caching} for more information.
|
||||
*
|
||||
* - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
|
||||
@@ -230,10 +270,11 @@ function $HttpProvider() {
|
||||
* - **`defaults.headers.put`**
|
||||
* - **`defaults.headers.patch`**
|
||||
*
|
||||
* - **`defaults.paramSerializer`** - {string|function(Object<string,string>):string} - A function used to prepare string representation
|
||||
* of request parameters (specified as an object).
|
||||
* If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
|
||||
* Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
|
||||
*
|
||||
* - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
|
||||
* used to the prepare string representation of request parameters (specified as an object).
|
||||
* If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
|
||||
* Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
|
||||
*
|
||||
**/
|
||||
var defaults = this.defaults = {
|
||||
@@ -699,15 +740,17 @@ function $HttpProvider() {
|
||||
* properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
|
||||
* or the per-request config object.
|
||||
*
|
||||
* In order to prevent collisions in environments where multiple Angular apps share the
|
||||
* same domain or subdomain, we recommend that each application uses unique cookie name.
|
||||
*
|
||||
*
|
||||
* @param {object} config Object describing the request to be made and how it should be
|
||||
* processed. The object has following properties:
|
||||
*
|
||||
* - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
|
||||
* - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
|
||||
* - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned
|
||||
* to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be
|
||||
* JSONified.
|
||||
* - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
|
||||
* with the `paramSerializer` and appended as GET parameters.
|
||||
* - **data** – `{string|Object}` – Data to be sent as the request message data.
|
||||
* - **headers** – `{Object}` – Map of strings or functions which return strings representing
|
||||
* HTTP headers to send to the server. If the return value of a function is null, the
|
||||
@@ -725,10 +768,14 @@ function $HttpProvider() {
|
||||
* transform function or an array of such functions. The transform function takes the http
|
||||
* response body, headers and status and returns its transformed (typically deserialized) version.
|
||||
* See {@link ng.$http#overriding-the-default-transformations-per-request
|
||||
* Overriding the Default Transformations}
|
||||
* - **paramSerializer** - {string|function(Object<string,string>):string} - A function used to prepare string representation
|
||||
* of request parameters (specified as an object).
|
||||
* Is specified as string, it is interpreted as function registered in with the {$injector}.
|
||||
* Overriding the Default TransformationjqLiks}
|
||||
* - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
|
||||
* prepare the string representation of request parameters (specified as an object).
|
||||
* If specified as string, it is interpreted as function registered with the
|
||||
* {@link $injector $injector}, which means you can create your own serializer
|
||||
* by registering it as a {@link auto.$provide#service service}.
|
||||
* The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
|
||||
* alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
|
||||
* - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
|
||||
* GET request, otherwise if a cache instance built with
|
||||
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
|
||||
@@ -739,7 +786,7 @@ function $HttpProvider() {
|
||||
* XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
|
||||
* for more information.
|
||||
* - **responseType** - `{string}` - see
|
||||
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
|
||||
* [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
|
||||
*
|
||||
* @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
|
||||
* standard `then` method and two http specific methods: `success` and `error`. The `then`
|
||||
|
||||
+10
-2
@@ -185,7 +185,7 @@ function LocationHashbangUrl(appBase, hashPrefix) {
|
||||
var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
|
||||
var withoutHashUrl;
|
||||
|
||||
if (withoutBaseUrl.charAt(0) === '#') {
|
||||
if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
|
||||
|
||||
// The rest of the url starts with a hash so we have
|
||||
// got either a hashbang path or a plain hash fragment
|
||||
@@ -199,7 +199,15 @@ function LocationHashbangUrl(appBase, hashPrefix) {
|
||||
// There was no hashbang path nor hash fragment:
|
||||
// If we are in HTML5 mode we use what is left as the path;
|
||||
// Otherwise we ignore what is left
|
||||
withoutHashUrl = this.$$html5 ? withoutBaseUrl : '';
|
||||
if (this.$$html5) {
|
||||
withoutHashUrl = withoutBaseUrl;
|
||||
} else {
|
||||
withoutHashUrl = '';
|
||||
if (isUndefined(withoutBaseUrl)) {
|
||||
appBase = url;
|
||||
this.replace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parseAppUrl(withoutHashUrl, this);
|
||||
|
||||
+6
-2
@@ -982,8 +982,10 @@ ASTCompiler.prototype = {
|
||||
nameId.name = ast.property.name;
|
||||
}
|
||||
}
|
||||
recursionFn(intoId);
|
||||
}, function() {
|
||||
self.assign(intoId, 'undefined');
|
||||
});
|
||||
recursionFn(intoId);
|
||||
}, !!create);
|
||||
break;
|
||||
case AST.CallExpression:
|
||||
@@ -1021,8 +1023,10 @@ ASTCompiler.prototype = {
|
||||
}
|
||||
expression = self.ensureSafeObject(expression);
|
||||
self.assign(intoId, expression);
|
||||
recursionFn(intoId);
|
||||
}, function() {
|
||||
self.assign(intoId, 'undefined');
|
||||
});
|
||||
recursionFn(intoId);
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
+14
@@ -497,6 +497,19 @@ function qFactory(nextTick, exceptionHandler) {
|
||||
return result.promise.then(callback, errback, progressBack);
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $q#resolve
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
|
||||
*
|
||||
* @param {*} value Value or a promise
|
||||
* @returns {Promise} Returns a promise of the passed value or promise
|
||||
*/
|
||||
var resolve = when;
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $q#all
|
||||
@@ -565,6 +578,7 @@ function qFactory(nextTick, exceptionHandler) {
|
||||
$Q.defer = defer;
|
||||
$Q.reject = reject;
|
||||
$Q.when = when;
|
||||
$Q.resolve = resolve;
|
||||
$Q.all = all;
|
||||
|
||||
return $Q;
|
||||
|
||||
@@ -7,12 +7,14 @@ var $compileMinErr = minErr('$compile');
|
||||
* @name $templateRequest
|
||||
*
|
||||
* @description
|
||||
* The `$templateRequest` service downloads the provided template using `$http` and, upon success,
|
||||
* stores the contents inside of `$templateCache`. If the HTTP request fails or the response data
|
||||
* of the HTTP request is empty, a `$compile` error will be thrown (the exception can be thwarted
|
||||
* by setting the 2nd parameter of the function to true).
|
||||
* The `$templateRequest` service runs security checks then downloads the provided template using
|
||||
* `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
|
||||
* fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
|
||||
* exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
|
||||
* contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
|
||||
* when `tpl` is of type string and `$templateCache` has the matching entry.
|
||||
*
|
||||
* @param {string} tpl The HTTP request template URL
|
||||
* @param {string|TrustedResourceUrl} tpl The HTTP request template URL
|
||||
* @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
|
||||
*
|
||||
* @return {Promise} a promise for the HTTP response data of the given URL.
|
||||
@@ -20,10 +22,19 @@ var $compileMinErr = minErr('$compile');
|
||||
* @property {number} totalPendingRequests total amount of pending template requests being downloaded.
|
||||
*/
|
||||
function $TemplateRequestProvider() {
|
||||
this.$get = ['$templateCache', '$http', '$q', function($templateCache, $http, $q) {
|
||||
this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
|
||||
function handleRequestFn(tpl, ignoreRequestError) {
|
||||
handleRequestFn.totalPendingRequests++;
|
||||
|
||||
// We consider the template cache holds only trusted templates, so
|
||||
// there's no need to go through whitelisting again for keys that already
|
||||
// are included in there. This also makes Angular accept any script
|
||||
// directive, no matter its name. However, we still need to unwrap trusted
|
||||
// types.
|
||||
if (!isString(tpl) || !$templateCache.get(tpl)) {
|
||||
tpl = $sce.getTrustedResourceUrl(tpl);
|
||||
}
|
||||
|
||||
var transformResponse = $http.defaults && $http.defaults.transformResponse;
|
||||
|
||||
if (isArray(transformResponse)) {
|
||||
|
||||
@@ -176,7 +176,8 @@
|
||||
* unless you really need a digest to kick off afterwards.
|
||||
*
|
||||
* Keep in mind that, to make this easier, ngAnimate has tweaked the JS animations API to recognize when a runner instance is returned from $animateCss
|
||||
* (so there is no need to call `runner.done(doneFn)` inside of your JavaScript animation code). Check the [animation code above](#usage) to see how this works.
|
||||
* (so there is no need to call `runner.done(doneFn)` inside of your JavaScript animation code).
|
||||
* Check the {@link ngAnimate.$animateCss#usage animation code above} to see how this works.
|
||||
*
|
||||
* @param {DOMElement} element the element that will be animated
|
||||
* @param {object} options the animation-related options that will be applied during the animation
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
* opacity:0;
|
||||
* }
|
||||
*
|
||||
* /* The starting CSS styles for the enter animation */
|
||||
* /* The finishing CSS styles for the enter animation */
|
||||
* .fade.ng-enter.ng-enter-active {
|
||||
* opacity:1;
|
||||
* }
|
||||
@@ -327,7 +327,7 @@
|
||||
* ## CSS + JS Animations Together
|
||||
*
|
||||
* AngularJS 1.4 and higher has taken steps to make the amalgamation of CSS and JS animations more flexible. However, unlike earlier versions of Angular,
|
||||
* defining CSS and JS animations to work off of the same CSS class will not work anymore. Therefore example below will only result in **JS animations taking
|
||||
* defining CSS and JS animations to work off of the same CSS class will not work anymore. Therefore the example below will only result in **JS animations taking
|
||||
* charge of the animation**:
|
||||
*
|
||||
* ```html
|
||||
@@ -356,8 +356,8 @@
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Does this mean that CSS and JS animations cannot be used together? Do JS-based animations always have higher priority? We can suppliment for the
|
||||
* lack of CSS animations by making use of the `$animateCss` service to trigger our own tweaked-out, CSS-based animations directly from
|
||||
* Does this mean that CSS and JS animations cannot be used together? Do JS-based animations always have higher priority? We can make up for the
|
||||
* lack of CSS animations by using the `$animateCss` service to trigger our own tweaked-out, CSS-based animations directly from
|
||||
* our own JS-based animation code:
|
||||
*
|
||||
* ```js
|
||||
@@ -655,7 +655,7 @@
|
||||
* ngModule.directive('greetingBox', ['$animate', function($animate) {
|
||||
* return function(scope, element, attrs) {
|
||||
* attrs.$observe('active', function(value) {
|
||||
* value ? $animate.addClass(element, 'on') ? $animate.removeClass(element, 'on');
|
||||
* value ? $animate.addClass(element, 'on') : $animate.removeClass(element, 'on');
|
||||
* });
|
||||
* });
|
||||
* }]);
|
||||
@@ -666,7 +666,7 @@
|
||||
*
|
||||
* ```css
|
||||
* /* normally we would create a CSS class to reference on the element */
|
||||
* [greeting-box].on { transition:0.5s linear all; background:green; color:white; }
|
||||
* greeting-box.on { transition:0.5s linear all; background:green; color:white; }
|
||||
* ```
|
||||
*
|
||||
* The `$animate` service contains a variety of other methods like `enter`, `leave`, `animate` and `setClass`. To learn more about what's
|
||||
|
||||
+27
-12
@@ -14,8 +14,8 @@
|
||||
*
|
||||
* ## Usage
|
||||
*
|
||||
* For ngAria to do its magic, simply include the module as a dependency. The directives supported
|
||||
* by ngAria are:
|
||||
* For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
|
||||
* directives are supported:
|
||||
* `ngModel`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`, `ngDblClick`, and `ngMessages`.
|
||||
*
|
||||
* Below is a more detailed breakdown of the attributes handled by ngAria:
|
||||
@@ -83,7 +83,8 @@ function $AriaProvider() {
|
||||
ariaMultiline: true,
|
||||
ariaValue: true,
|
||||
tabindex: true,
|
||||
bindKeypress: true
|
||||
bindKeypress: true,
|
||||
bindRoleForClick: true
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -102,6 +103,8 @@ function $AriaProvider() {
|
||||
* - **tabindex** – `{boolean}` – Enables/disables tabindex tags
|
||||
* - **bindKeypress** – `{boolean}` – Enables/disables keypress event binding on `<div>` and
|
||||
* `<li>` elements with ng-click
|
||||
* - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements like `div`
|
||||
* using ng-click, making them more accessible to users of assistive technologies
|
||||
*
|
||||
* @description
|
||||
* Enables/disables various ARIA attributes
|
||||
@@ -115,9 +118,8 @@ function $AriaProvider() {
|
||||
var ariaCamelName = attr.$normalize(ariaAttr);
|
||||
if (config[ariaCamelName] && !attr[ariaCamelName]) {
|
||||
scope.$watch(attr[attrName], function(boolVal) {
|
||||
if (negate) {
|
||||
boolVal = !boolVal;
|
||||
}
|
||||
// ensure boolean value
|
||||
boolVal = negate ? !boolVal : !!boolVal;
|
||||
elem.attr(ariaAttr, boolVal);
|
||||
});
|
||||
}
|
||||
@@ -265,13 +267,23 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
|
||||
elem.attr('role', 'slider');
|
||||
}
|
||||
if ($aria.config('ariaValue')) {
|
||||
if (attr.min && !elem.attr('aria-valuemin')) {
|
||||
elem.attr('aria-valuemin', attr.min);
|
||||
var needsAriaValuemin = !elem.attr('aria-valuemin') &&
|
||||
(attr.hasOwnProperty('min') || attr.hasOwnProperty('ngMin'));
|
||||
var needsAriaValuemax = !elem.attr('aria-valuemax') &&
|
||||
(attr.hasOwnProperty('max') || attr.hasOwnProperty('ngMax'));
|
||||
var needsAriaValuenow = !elem.attr('aria-valuenow');
|
||||
|
||||
if (needsAriaValuemin) {
|
||||
attr.$observe('min', function ngAriaValueMinReaction(newVal) {
|
||||
elem.attr('aria-valuemin', newVal);
|
||||
});
|
||||
}
|
||||
if (attr.max && !elem.attr('aria-valuemax')) {
|
||||
elem.attr('aria-valuemax', attr.max);
|
||||
if (needsAriaValuemax) {
|
||||
attr.$observe('max', function ngAriaValueMinReaction(newVal) {
|
||||
elem.attr('aria-valuemax', newVal);
|
||||
});
|
||||
}
|
||||
if (!elem.attr('aria-valuenow')) {
|
||||
if (needsAriaValuenow) {
|
||||
scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) {
|
||||
elem.attr('aria-valuenow', newVal);
|
||||
});
|
||||
@@ -337,7 +349,10 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!elem.attr('role') && !isNodeOneOf(elem, nodeBlackList)) {
|
||||
|
||||
if ($aria.config('bindRoleForClick')
|
||||
&& !elem.attr('role')
|
||||
&& !isNodeOneOf(elem, nodeBlackList)) {
|
||||
elem.attr('role', 'button');
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ angular.module('ngCookies').
|
||||
* Requires the {@link ngCookies `ngCookies`} module to be installed.
|
||||
*
|
||||
* <div class="alert alert-danger">
|
||||
* **Note:** The $cookieStore service is deprecated.
|
||||
* **Note:** The $cookieStore service is **deprecated**.
|
||||
* Please use the {@link ngCookies.$cookies `$cookies`} service instead.
|
||||
* </div>
|
||||
*
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
*
|
||||
* <div doc-module-components="ngCookies"></div>
|
||||
*
|
||||
* See {@link ngCookies.$cookies `$cookies`} and
|
||||
* {@link ngCookies.$cookieStore `$cookieStore`} for usage.
|
||||
* See {@link ngCookies.$cookies `$cookies`} for usage.
|
||||
*/
|
||||
|
||||
|
||||
@@ -43,7 +42,7 @@ angular.module('ngCookies', ['ng']).
|
||||
* or a Date object indicating the exact date/time this cookie will expire.
|
||||
* - **secure** - `{boolean}` - The cookie will be available only in secured connection.
|
||||
*
|
||||
* Note: by default the address that appears in your <base> tag will be used as path.
|
||||
* Note: by default the address that appears in your `<base>` tag will be used as path.
|
||||
* This is import so that cookies will be visible for all routes in case html5mode is enabled
|
||||
*
|
||||
**/
|
||||
@@ -60,9 +59,11 @@ angular.module('ngCookies', ['ng']).
|
||||
* @description
|
||||
* Provides read/write access to browser's cookies.
|
||||
*
|
||||
* BREAKING CHANGE: `$cookies` no longer exposes properties that represent the
|
||||
* current browser cookie values. Now you must use the get/put/remove/etc. methods
|
||||
* as described below.
|
||||
* <div class="alert alert-info">
|
||||
* Up until Angular 1.3, `$cookies` exposed properties that represented the
|
||||
* current browser cookie values. In version 1.4, this behavior has changed, and
|
||||
* `$cookies` now provides a standard api of getters, setters etc.
|
||||
* </div>
|
||||
*
|
||||
* Requires the {@link ngCookies `ngCookies`} module to be installed.
|
||||
*
|
||||
|
||||
@@ -51,7 +51,7 @@ var jqLite = angular.element;
|
||||
*
|
||||
* ```javascript
|
||||
* <!-- keep in mind that ngModel automatically sets these error flags -->
|
||||
* myField.$error = { minlength : true, required : false };
|
||||
* myField.$error = { minlength : true, required : true };
|
||||
* ```
|
||||
*
|
||||
* Then the `required` message will be displayed first. When required is false then the `minlength` message
|
||||
@@ -251,7 +251,7 @@ angular.module('ngMessages', [])
|
||||
*
|
||||
* @description
|
||||
* `ngMessages` is a directive that is designed to show and hide messages based on the state
|
||||
* of a key/value object that it listens on. The directive itself compliments error message
|
||||
* of a key/value object that it listens on. The directive itself complements error message
|
||||
* reporting with the `ngModel` $error object (which stores a key/value state of validation errors).
|
||||
*
|
||||
* `ngMessages` manages the state of internal messages within its container element. The internal
|
||||
|
||||
Vendored
+1
-1
@@ -1083,7 +1083,7 @@ angular.mock.dump = function(object) {
|
||||
$httpBackend.flush();
|
||||
|
||||
$httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
|
||||
// check if the header was send, if it wasn't the expectation won't
|
||||
// check if the header was sent, if it wasn't the expectation won't
|
||||
// match the request and the test will fail
|
||||
return headers['Authorization'] == 'xxx';
|
||||
}).respond(201, '');
|
||||
|
||||
@@ -209,7 +209,8 @@ function shallowClearAndCopy(src, dst) {
|
||||
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
|
||||
*
|
||||
*
|
||||
* Success callback is called with (value, responseHeaders) arguments. Error callback is called
|
||||
* Success callback is called with (value, responseHeaders) arguments, where the value is
|
||||
* the populated resource instance or collection object. The error callback is called
|
||||
* with (httpResponse) argument.
|
||||
*
|
||||
* Class actions return empty instance (with additional properties below).
|
||||
|
||||
@@ -591,9 +591,8 @@ function $RouteProvider() {
|
||||
if (angular.isFunction(templateUrl)) {
|
||||
templateUrl = templateUrl(nextRoute.params);
|
||||
}
|
||||
templateUrl = $sce.getTrustedResourceUrl(templateUrl);
|
||||
if (angular.isDefined(templateUrl)) {
|
||||
nextRoute.loadedTemplateUrl = templateUrl;
|
||||
nextRoute.loadedTemplateUrl = $sce.valueOf(templateUrl);
|
||||
template = $templateRequest(templateUrl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,8 +104,8 @@
|
||||
*/
|
||||
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
var LINKY_URL_REGEXP =
|
||||
/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"”’]/,
|
||||
MAILTO_REGEXP = /^mailto:/;
|
||||
/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"”’]/i,
|
||||
MAILTO_REGEXP = /^mailto:/i;
|
||||
|
||||
return function(text, target) {
|
||||
if (!text) return text;
|
||||
|
||||
@@ -228,7 +228,7 @@ var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap,xlink:href");
|
||||
var htmlAttrs = makeMap('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,target,title,type,' +
|
||||
'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
|
||||
'valign,value,vspace,width');
|
||||
|
||||
// SVG attributes (without "id" and "name" attributes)
|
||||
|
||||
@@ -74,7 +74,8 @@ ngTouch.factory('$swipe', [function() {
|
||||
* `$swipe` will listen for `mouse` and `touch` 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 }`.
|
||||
* 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
|
||||
|
||||
+85
-2
@@ -336,7 +336,7 @@ describe('angular', function() {
|
||||
expect(hashKey(dst)).not.toEqual(hashKey(src));
|
||||
});
|
||||
|
||||
it('should retain the previous $$hashKey', function() {
|
||||
it('should retain the previous $$hashKey when copying object with hashKey', function() {
|
||||
var src,dst,h;
|
||||
src = {};
|
||||
dst = {};
|
||||
@@ -351,7 +351,21 @@ describe('angular', function() {
|
||||
expect(hashKey(dst)).toEqual(h);
|
||||
});
|
||||
|
||||
it('should handle circular references when circularRefs is turned on', function() {
|
||||
it('should retain the previous $$hashKey when copying non-object', function() {
|
||||
var dst = {};
|
||||
var h = hashKey(dst);
|
||||
|
||||
copy(null, dst);
|
||||
expect(hashKey(dst)).toEqual(h);
|
||||
|
||||
copy(42, dst);
|
||||
expect(hashKey(dst)).toEqual(h);
|
||||
|
||||
copy(new Date(), dst);
|
||||
expect(hashKey(dst)).toEqual(h);
|
||||
});
|
||||
|
||||
it('should handle circular references', function() {
|
||||
var a = {b: {a: null}, self: null, selfs: [null, null, [null]]};
|
||||
a.b.a = a;
|
||||
a.self = a;
|
||||
@@ -362,13 +376,60 @@ describe('angular', function() {
|
||||
|
||||
expect(aCopy).not.toBe(a);
|
||||
expect(aCopy).toBe(aCopy.self);
|
||||
expect(aCopy).toBe(aCopy.selfs[2][0]);
|
||||
expect(aCopy.selfs[2]).not.toBe(a.selfs[2]);
|
||||
|
||||
var copyTo = [];
|
||||
aCopy = copy(a, copyTo);
|
||||
expect(aCopy).toBe(copyTo);
|
||||
expect(aCopy).not.toBe(a);
|
||||
expect(aCopy).toBe(aCopy.self);
|
||||
});
|
||||
|
||||
it('should handle objects with multiple references', function() {
|
||||
var b = {};
|
||||
var a = [b, -1, b];
|
||||
|
||||
var aCopy = copy(a);
|
||||
expect(aCopy[0]).not.toBe(a[0]);
|
||||
expect(aCopy[0]).toBe(aCopy[2]);
|
||||
|
||||
var copyTo = [];
|
||||
aCopy = copy(a, copyTo);
|
||||
expect(aCopy).toBe(copyTo);
|
||||
expect(aCopy[0]).not.toBe(a[0]);
|
||||
expect(aCopy[0]).toBe(aCopy[2]);
|
||||
});
|
||||
|
||||
it('should handle date/regex objects with multiple references', function() {
|
||||
var re = /foo/;
|
||||
var d = new Date();
|
||||
var o = {re: re, re2: re, d: d, d2: d};
|
||||
|
||||
var oCopy = copy(o);
|
||||
expect(oCopy.re).toBe(oCopy.re2);
|
||||
expect(oCopy.d).toBe(oCopy.d2);
|
||||
|
||||
oCopy = copy(o, {});
|
||||
expect(oCopy.re).toBe(oCopy.re2);
|
||||
expect(oCopy.d).toBe(oCopy.d2);
|
||||
});
|
||||
|
||||
it('should clear destination arrays correctly when source is non-array', function() {
|
||||
expect(copy(null, [1,2,3])).toEqual([]);
|
||||
expect(copy(undefined, [1,2,3])).toEqual([]);
|
||||
expect(copy({0: 1, 1: 2}, [1,2,3])).toEqual([1,2]);
|
||||
expect(copy(new Date(), [1,2,3])).toEqual([]);
|
||||
expect(copy(/a/, [1,2,3])).toEqual([]);
|
||||
expect(copy(true, [1,2,3])).toEqual([]);
|
||||
});
|
||||
|
||||
it('should clear destination objects correctly when source is non-array', function() {
|
||||
expect(copy(null, {0:1,1:2,2:3})).toEqual({});
|
||||
expect(copy(undefined, {0:1,1:2,2:3})).toEqual({});
|
||||
expect(copy(new Date(), {0:1,1:2,2:3})).toEqual({});
|
||||
expect(copy(/a/, {0:1,1:2,2:3})).toEqual({});
|
||||
expect(copy(true, {0:1,1:2,2:3})).toEqual({});
|
||||
});
|
||||
|
||||
it('should copy objects with no prototype parent', function() {
|
||||
@@ -420,6 +481,16 @@ describe('angular', function() {
|
||||
// make sure we retain the old key
|
||||
expect(hashKey(dst)).toEqual(h);
|
||||
});
|
||||
|
||||
|
||||
it('should copy dates by reference', function() {
|
||||
var src = { date: new Date() };
|
||||
var dst = {};
|
||||
|
||||
extend(dst, src);
|
||||
|
||||
expect(dst.date).toBe(src.date);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -487,6 +558,18 @@ describe('angular', function() {
|
||||
});
|
||||
expect(dst.foo).not.toBe(src.foo);
|
||||
});
|
||||
|
||||
|
||||
it('should copy dates by value', function() {
|
||||
var src = { date: new Date() };
|
||||
var dst = {};
|
||||
|
||||
merge(dst, src);
|
||||
|
||||
expect(dst.date).not.toBe(src.date);
|
||||
expect(isDate(dst.date)).toBeTruthy();
|
||||
expect(dst.date.valueOf()).toEqual(src.date.valueOf());
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -405,10 +405,13 @@ describe('jqLite', function() {
|
||||
it('should provide the non-wrapped data calls', function() {
|
||||
var node = document.createElement('div');
|
||||
|
||||
expect(jqLite.hasData(node)).toBe(false);
|
||||
expect(jqLite.data(node, "foo")).toBeUndefined();
|
||||
expect(jqLite.hasData(node)).toBe(false);
|
||||
|
||||
jqLite.data(node, "foo", "bar");
|
||||
|
||||
expect(jqLite.hasData(node)).toBe(true);
|
||||
expect(jqLite.data(node, "foo")).toBe("bar");
|
||||
expect(jqLite(node).data("foo")).toBe("bar");
|
||||
|
||||
@@ -421,6 +424,9 @@ describe('jqLite', function() {
|
||||
jqLite.removeData(node);
|
||||
jqLite.removeData(node);
|
||||
expect(jqLite.data(node, "bar")).toBeUndefined();
|
||||
|
||||
jqLite(node).remove();
|
||||
expect(jqLite.hasData(node)).toBe(false);
|
||||
});
|
||||
|
||||
it('should emit $destroy event if element removed via remove()', function() {
|
||||
|
||||
@@ -57,7 +57,9 @@ function MockWindow(options) {
|
||||
return getHash(locationHref);
|
||||
},
|
||||
set hash(value) {
|
||||
locationHref = stripHash(locationHref) + '#' + value;
|
||||
// replace the hash with the new one (stripping off a leading hash if there is one)
|
||||
// See hash setter spec: https://url.spec.whatwg.org/#urlutils-and-urlutilsreadonly-members
|
||||
locationHref = stripHash(locationHref) + '#' + value.replace(/^#/,'');
|
||||
},
|
||||
replace: function(url) {
|
||||
locationHref = url;
|
||||
|
||||
+300
-6
@@ -1296,14 +1296,24 @@ describe('$compile', function() {
|
||||
));
|
||||
|
||||
it('should not load cross domain templates by default', inject(
|
||||
function($compile, $rootScope, $templateCache, $sce) {
|
||||
function($compile, $rootScope) {
|
||||
expect(function() {
|
||||
$templateCache.put('http://example.com/should-not-load.html', 'Should not load even if in cache.');
|
||||
$compile('<div class="crossDomainTemplate"></div>')($rootScope);
|
||||
}).toThrowMinErr('$sce', 'insecurl', 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: http://example.com/should-not-load.html');
|
||||
}
|
||||
));
|
||||
|
||||
it('should trust what is already in the template cache', inject(
|
||||
function($compile, $httpBackend, $rootScope, $templateCache) {
|
||||
$httpBackend.expect('GET', 'http://example.com/should-not-load.html').respond('<span>example.com/remote-version</span>');
|
||||
$templateCache.put('http://example.com/should-not-load.html', '<span>example.com/cached-version</span>');
|
||||
element = $compile('<div class="crossDomainTemplate"></div>')($rootScope);
|
||||
expect(sortedHtml(element)).toEqual('<div class="crossDomainTemplate"></div>');
|
||||
$rootScope.$digest();
|
||||
expect(sortedHtml(element)).toEqual('<div class="crossDomainTemplate"><span>example.com/cached-version</span></div>');
|
||||
}
|
||||
));
|
||||
|
||||
it('should load cross domain templates when trusted', inject(
|
||||
function($compile, $httpBackend, $rootScope, $sce) {
|
||||
$httpBackend.expect('GET', 'http://example.com/trusted-template.html').respond('<span>example.com/trusted_template_contents</span>');
|
||||
@@ -2231,6 +2241,45 @@ describe('$compile', function() {
|
||||
}}
|
||||
};
|
||||
});
|
||||
directive('prototypeMethodNameAsScopeVarA', function() {
|
||||
return {
|
||||
scope: {
|
||||
'constructor': '=?',
|
||||
'valueOf': '='
|
||||
},
|
||||
restrict: 'AE',
|
||||
template: '<span></span>'
|
||||
};
|
||||
});
|
||||
directive('prototypeMethodNameAsScopeVarB', function() {
|
||||
return {
|
||||
scope: {
|
||||
'constructor': '@?',
|
||||
'valueOf': '@'
|
||||
},
|
||||
restrict: 'AE',
|
||||
template: '<span></span>'
|
||||
};
|
||||
});
|
||||
directive('prototypeMethodNameAsScopeVarC', function() {
|
||||
return {
|
||||
scope: {
|
||||
'constructor': '&?',
|
||||
'valueOf': '&'
|
||||
},
|
||||
restrict: 'AE',
|
||||
template: '<span></span>'
|
||||
};
|
||||
});
|
||||
directive('watchAsScopeVar', function() {
|
||||
return {
|
||||
scope: {
|
||||
'watch': '='
|
||||
},
|
||||
restrict: 'AE',
|
||||
template: '<span></span>'
|
||||
};
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
@@ -2331,7 +2380,7 @@ describe('$compile', function() {
|
||||
})
|
||||
);
|
||||
|
||||
it('should not allow more then one isolate scope creation per element', inject(
|
||||
it('should not allow more than one isolate scope creation per element', inject(
|
||||
function($rootScope, $compile) {
|
||||
expect(function() {
|
||||
$compile('<div class="iscope-a; scope-b"></div>');
|
||||
@@ -2340,6 +2389,18 @@ describe('$compile', function() {
|
||||
})
|
||||
);
|
||||
|
||||
it('should not allow more than one isolate/new scope creation per element regardless of `templateUrl`',
|
||||
inject(function($httpBackend) {
|
||||
$httpBackend.expect('GET', 'tiscope.html').respond('<div>Hello, world !</div>');
|
||||
|
||||
expect(function() {
|
||||
compile('<div class="tiscope-a; scope-b"></div>');
|
||||
$httpBackend.flush();
|
||||
}).toThrowMinErr('$compile', 'multidir', 'Multiple directives [scopeB, tiscopeA] ' +
|
||||
'asking for new/isolated scope on: <div class="tiscope-a; scope-b ng-scope">');
|
||||
})
|
||||
);
|
||||
|
||||
it('should not allow more than one isolate scope creation per element regardless of directive priority', function() {
|
||||
module(function($compileProvider) {
|
||||
$compileProvider.directive('highPriorityScope', function() {
|
||||
@@ -2472,6 +2533,118 @@ describe('$compile', function() {
|
||||
expect(element.isolateScope()).not.toBe($rootScope);
|
||||
})
|
||||
);
|
||||
|
||||
it('should handle "=" bindings with same method names in Object.prototype correctly when not present', inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-a></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
|
||||
expect(func).not.toThrow();
|
||||
expect(element.find('span').scope()).toBe(element.isolateScope());
|
||||
expect(element.isolateScope()).not.toBe($rootScope);
|
||||
expect(element.isolateScope()['constructor']).toBe($rootScope.constructor);
|
||||
expect(element.isolateScope()['valueOf']).toBeUndefined();
|
||||
})
|
||||
);
|
||||
|
||||
it('should handle "=" bindings with same method names in Object.prototype correctly when present', inject(
|
||||
function($rootScope, $compile) {
|
||||
$rootScope.constructor = 'constructor';
|
||||
$rootScope.valueOf = 'valueOf';
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-a constructor="constructor" value-of="valueOf"></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
|
||||
expect(func).not.toThrow();
|
||||
expect(element.find('span').scope()).toBe(element.isolateScope());
|
||||
expect(element.isolateScope()).not.toBe($rootScope);
|
||||
expect(element.isolateScope()['constructor']).toBe('constructor');
|
||||
expect(element.isolateScope()['valueOf']).toBe('valueOf');
|
||||
})
|
||||
);
|
||||
|
||||
it('should handle "@" bindings with same method names in Object.prototype correctly when not present', inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile('<div prototype-method-name-as-scope-var-b></div>')($rootScope);
|
||||
};
|
||||
|
||||
expect(func).not.toThrow();
|
||||
expect(element.find('span').scope()).toBe(element.isolateScope());
|
||||
expect(element.isolateScope()).not.toBe($rootScope);
|
||||
expect(element.isolateScope()['constructor']).toBe($rootScope.constructor);
|
||||
expect(element.isolateScope()['valueOf']).toBeUndefined();
|
||||
})
|
||||
);
|
||||
|
||||
it('should handle "@" bindings with same method names in Object.prototype correctly when present', inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-b constructor="constructor" value-of="valueOf"></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
|
||||
expect(func).not.toThrow();
|
||||
expect(element.find('span').scope()).toBe(element.isolateScope());
|
||||
expect(element.isolateScope()).not.toBe($rootScope);
|
||||
expect(element.isolateScope()['constructor']).toBe('constructor');
|
||||
expect(element.isolateScope()['valueOf']).toBe('valueOf');
|
||||
})
|
||||
);
|
||||
|
||||
it('should handle "&" bindings with same method names in Object.prototype correctly when not present', inject(
|
||||
function($rootScope, $compile) {
|
||||
var func = function() {
|
||||
element = $compile('<div prototype-method-name-as-scope-var-c></div>')($rootScope);
|
||||
};
|
||||
|
||||
expect(func).not.toThrow();
|
||||
expect(element.find('span').scope()).toBe(element.isolateScope());
|
||||
expect(element.isolateScope()).not.toBe($rootScope);
|
||||
expect(element.isolateScope()['constructor']).toBe($rootScope.constructor);
|
||||
expect(element.isolateScope()['valueOf']()).toBeUndefined();
|
||||
})
|
||||
);
|
||||
|
||||
it('should handle "&" bindings with same method names in Object.prototype correctly when present', inject(
|
||||
function($rootScope, $compile) {
|
||||
$rootScope.constructor = function() { return 'constructor'; };
|
||||
$rootScope.valueOf = function() { return 'valueOf'; };
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div prototype-method-name-as-scope-var-c constructor="constructor()" value-of="valueOf()"></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
|
||||
expect(func).not.toThrow();
|
||||
expect(element.find('span').scope()).toBe(element.isolateScope());
|
||||
expect(element.isolateScope()).not.toBe($rootScope);
|
||||
expect(element.isolateScope()['constructor']()).toBe('constructor');
|
||||
expect(element.isolateScope()['valueOf']()).toBe('valueOf');
|
||||
})
|
||||
);
|
||||
|
||||
it('should not throw exception when using "watch" as binding in Firefox', inject(
|
||||
function($rootScope, $compile) {
|
||||
$rootScope.watch = 'watch';
|
||||
var func = function() {
|
||||
element = $compile(
|
||||
'<div watch-as-scope-var watch="watch"></div>'
|
||||
)($rootScope);
|
||||
};
|
||||
|
||||
expect(func).not.toThrow();
|
||||
expect(element.find('span').scope()).toBe(element.isolateScope());
|
||||
expect(element.isolateScope()).not.toBe($rootScope);
|
||||
expect(element.isolateScope()['watch']).toBe('watch');
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -2514,6 +2687,70 @@ describe('$compile', function() {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('multidir isolated scope error messages', function() {
|
||||
angular.module('fakeIsoledScopeModule', [])
|
||||
.directive('fakeScope', function(log) {
|
||||
return {
|
||||
scope: true,
|
||||
restrict: 'CA',
|
||||
compile: function() {
|
||||
return {pre: function(scope, element) {
|
||||
log(scope.$id);
|
||||
expect(element.data('$scope')).toBe(scope);
|
||||
}};
|
||||
}
|
||||
};
|
||||
})
|
||||
.directive('fakeIScope', function(log) {
|
||||
return {
|
||||
scope: {},
|
||||
restrict: 'CA',
|
||||
compile: function() {
|
||||
return function(scope, element) {
|
||||
iscope = scope;
|
||||
log(scope.$id);
|
||||
expect(element.data('$isolateScopeNoTemplate')).toBe(scope);
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(module('fakeIsoledScopeModule', function() {
|
||||
directive('anonymModuleScopeDirective', function(log) {
|
||||
return {
|
||||
scope: true,
|
||||
restrict: 'CA',
|
||||
compile: function() {
|
||||
return {pre: function(scope, element) {
|
||||
log(scope.$id);
|
||||
expect(element.data('$scope')).toBe(scope);
|
||||
}};
|
||||
}
|
||||
};
|
||||
});
|
||||
}));
|
||||
|
||||
it('should add module name to multidir isolated scope message if directive defined through module', inject(
|
||||
function($rootScope, $compile) {
|
||||
expect(function() {
|
||||
$compile('<div class="fake-scope; fake-i-scope"></div>');
|
||||
}).toThrowMinErr('$compile', 'multidir',
|
||||
'Multiple directives [fakeIScope (module: fakeIsoledScopeModule), fakeScope (module: fakeIsoledScopeModule)] ' +
|
||||
'asking for new/isolated scope on: <div class="fake-scope; fake-i-scope">');
|
||||
})
|
||||
);
|
||||
|
||||
it('sholdn\'t add module name to multidir isolated scope message if directive is defined directly with $compileProvider', inject(
|
||||
function($rootScope, $compile) {
|
||||
expect(function() {
|
||||
$compile('<div class="anonym-module-scope-directive; fake-i-scope"></div>');
|
||||
}).toThrowMinErr('$compile', 'multidir',
|
||||
'Multiple directives [anonymModuleScopeDirective, fakeIScope (module: fakeIsoledScopeModule)] ' +
|
||||
'asking for new/isolated scope on: <div class="anonym-module-scope-directive; fake-i-scope">');
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2777,6 +3014,25 @@ describe('$compile', function() {
|
||||
}));
|
||||
|
||||
|
||||
it('should handle consecutive text elements as a single text element', inject(function($rootScope, $compile) {
|
||||
// No point it running the test, if there is no MutationObserver
|
||||
if (!window.MutationObserver) return;
|
||||
|
||||
// Create and register the MutationObserver
|
||||
var observer = new window.MutationObserver(noop);
|
||||
observer.observe(document.body, {childList: true, subtree: true});
|
||||
|
||||
// Run the actual test
|
||||
var base = jqLite('<div>— {{ "This doesn\'t." }}</div>');
|
||||
element = $compile(base)($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.text()).toBe("— This doesn't.");
|
||||
|
||||
// Unregister the MutationObserver (and hope it doesn't mess up with subsequent tests)
|
||||
observer.disconnect();
|
||||
}));
|
||||
|
||||
|
||||
it('should support custom start/end interpolation symbols in template and directive template',
|
||||
function() {
|
||||
module(function($interpolateProvider, $compileProvider) {
|
||||
@@ -4358,6 +4614,41 @@ describe('$compile', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should correctly assign controller return values for multiple directives', function() {
|
||||
var directiveController, otherDirectiveController;
|
||||
module(function() {
|
||||
|
||||
directive('myDirective', function(log) {
|
||||
return {
|
||||
scope: true,
|
||||
controller: function($scope) {
|
||||
return directiveController = {
|
||||
foo: 'bar'
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
directive('myOtherDirective', function(log) {
|
||||
return {
|
||||
controller: function($scope) {
|
||||
return otherDirectiveController = {
|
||||
baz: 'luh'
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
inject(function(log, $compile, $rootScope) {
|
||||
element = $compile('<my-directive my-other-directive></my-directive>')($rootScope);
|
||||
expect(element.data('$myDirectiveController')).toBe(directiveController);
|
||||
expect(element.data('$myOtherDirectiveController')).toBe(otherDirectiveController);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should get required parent controller', function() {
|
||||
module(function() {
|
||||
directive('nested', function(log) {
|
||||
@@ -5358,8 +5649,13 @@ describe('$compile', function() {
|
||||
expect(privateData.events.click).toBeDefined();
|
||||
expect(privateData.events.click[0]).toBeDefined();
|
||||
|
||||
//Ensure the angular $destroy event is still sent
|
||||
var destroyCount = 0;
|
||||
element.find("div").on("$destroy", function() { destroyCount++; });
|
||||
|
||||
$rootScope.$apply('xs = null');
|
||||
|
||||
expect(destroyCount).toBe(2);
|
||||
expect(firstRepeatedElem.data('$scope')).not.toBeDefined();
|
||||
privateData = jQuery._data(firstRepeatedElem[0]);
|
||||
expect(privateData && privateData.events).not.toBeDefined();
|
||||
@@ -5380,9 +5676,7 @@ describe('$compile', function() {
|
||||
|
||||
testCleanup();
|
||||
|
||||
// The initial ng-repeat div is dumped after parsing hence we expect cleanData
|
||||
// count to be one larger than size of the iterated array.
|
||||
expect(cleanedCount).toBe(xs.length + 1);
|
||||
expect(cleanedCount).toBe(xs.length);
|
||||
|
||||
// Restore the previous jQuery.cleanData.
|
||||
jQuery.cleanData = currentCleanData;
|
||||
|
||||
@@ -1917,6 +1917,71 @@ describe('input', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should parse exponential notation', function() {
|
||||
var inputElm = helper.compileInput('<input type="number" name="alias" ng-model="value" />');
|
||||
|
||||
// #.###e+##
|
||||
$rootScope.form.alias.$setViewValue("1.23214124123412412e+26");
|
||||
expect(inputElm).toBeValid();
|
||||
expect($rootScope.value).toBe(1.23214124123412412e+26);
|
||||
|
||||
// #.###e##
|
||||
$rootScope.form.alias.$setViewValue("1.23214124123412412e26");
|
||||
expect(inputElm).toBeValid();
|
||||
expect($rootScope.value).toBe(1.23214124123412412e26);
|
||||
|
||||
// #.###e-##
|
||||
$rootScope.form.alias.$setViewValue("1.23214124123412412e-26");
|
||||
expect(inputElm).toBeValid();
|
||||
expect($rootScope.value).toBe(1.23214124123412412e-26);
|
||||
|
||||
// ####e+##
|
||||
$rootScope.form.alias.$setViewValue("123214124123412412e+26");
|
||||
expect(inputElm).toBeValid();
|
||||
expect($rootScope.value).toBe(123214124123412412e26);
|
||||
|
||||
// ####e##
|
||||
$rootScope.form.alias.$setViewValue("123214124123412412e26");
|
||||
expect(inputElm).toBeValid();
|
||||
expect($rootScope.value).toBe(123214124123412412e26);
|
||||
|
||||
// ####e-##
|
||||
$rootScope.form.alias.$setViewValue("123214124123412412e-26");
|
||||
expect(inputElm).toBeValid();
|
||||
expect($rootScope.value).toBe(123214124123412412e-26);
|
||||
|
||||
// #.###E+##
|
||||
$rootScope.form.alias.$setViewValue("1.23214124123412412E+26");
|
||||
expect(inputElm).toBeValid();
|
||||
expect($rootScope.value).toBe(1.23214124123412412e+26);
|
||||
|
||||
// #.###E##
|
||||
$rootScope.form.alias.$setViewValue("1.23214124123412412E26");
|
||||
expect(inputElm).toBeValid();
|
||||
expect($rootScope.value).toBe(1.23214124123412412e26);
|
||||
|
||||
// #.###E-##
|
||||
$rootScope.form.alias.$setViewValue("1.23214124123412412E-26");
|
||||
expect(inputElm).toBeValid();
|
||||
expect($rootScope.value).toBe(1.23214124123412412e-26);
|
||||
|
||||
// ####E+##
|
||||
$rootScope.form.alias.$setViewValue("123214124123412412E+26");
|
||||
expect(inputElm).toBeValid();
|
||||
expect($rootScope.value).toBe(123214124123412412e26);
|
||||
|
||||
// ####E##
|
||||
$rootScope.form.alias.$setViewValue("123214124123412412E26");
|
||||
expect(inputElm).toBeValid();
|
||||
expect($rootScope.value).toBe(123214124123412412e26);
|
||||
|
||||
// ####E-##
|
||||
$rootScope.form.alias.$setViewValue("123214124123412412E-26");
|
||||
expect(inputElm).toBeValid();
|
||||
expect($rootScope.value).toBe(123214124123412412e-26);
|
||||
});
|
||||
|
||||
|
||||
describe('min', function() {
|
||||
|
||||
it('should validate', function() {
|
||||
|
||||
@@ -1091,6 +1091,31 @@ describe('ngModel', function() {
|
||||
}));
|
||||
|
||||
|
||||
it('should be possible to extend Object prototype and still be able to do form validation',
|
||||
inject(function($compile, $rootScope) {
|
||||
Object.prototype.someThing = function() {};
|
||||
var element = $compile('<form name="myForm">' +
|
||||
'<input type="text" name="username" ng-model="username" minlength="10" required />' +
|
||||
'</form>')($rootScope);
|
||||
var inputElm = element.find('input');
|
||||
|
||||
var formCtrl = $rootScope.myForm;
|
||||
var usernameCtrl = formCtrl.username;
|
||||
|
||||
$rootScope.$digest();
|
||||
expect(usernameCtrl.$invalid).toBe(true);
|
||||
expect(formCtrl.$invalid).toBe(true);
|
||||
|
||||
usernameCtrl.$setViewValue('valid-username');
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(usernameCtrl.$invalid).toBe(false);
|
||||
expect(formCtrl.$invalid).toBe(false);
|
||||
delete Object.prototype.someThing;
|
||||
|
||||
dealoc(element);
|
||||
}));
|
||||
|
||||
it('should re-evaluate the form validity state once the asynchronous promise has been delivered',
|
||||
inject(function($compile, $rootScope, $q) {
|
||||
|
||||
|
||||
@@ -448,6 +448,43 @@ describe('ngOptions', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should not watch non-numeric array properties', function() {
|
||||
createSelect({
|
||||
'ng-options': 'value as createLabel(value) for value in array',
|
||||
'ng-model': 'selected'
|
||||
});
|
||||
scope.createLabel = jasmine.createSpy('createLabel').andCallFake(function(value) { return value; });
|
||||
scope.array = ['a', 'b', 'c'];
|
||||
scope.array.$$private = 'do not watch';
|
||||
scope.array.$property = 'do not watch';
|
||||
scope.array.other = 'do not watch';
|
||||
scope.array.fn = function() {};
|
||||
scope.selected = 'b';
|
||||
scope.$digest();
|
||||
|
||||
expect(scope.createLabel).toHaveBeenCalledWith('a');
|
||||
expect(scope.createLabel).toHaveBeenCalledWith('b');
|
||||
expect(scope.createLabel).toHaveBeenCalledWith('c');
|
||||
expect(scope.createLabel).not.toHaveBeenCalledWith('do not watch');
|
||||
expect(scope.createLabel).not.toHaveBeenCalledWith(jasmine.any(Function));
|
||||
});
|
||||
|
||||
|
||||
it('should not watch object properties that start with $ or $$', function() {
|
||||
createSelect({
|
||||
'ng-options': 'key as createLabel(key) for (key, value) in object',
|
||||
'ng-model': 'selected'
|
||||
});
|
||||
scope.createLabel = jasmine.createSpy('createLabel').andCallFake(function(value) { return value; });
|
||||
scope.object = {'regularProperty': 'visible', '$$private': 'invisible', '$property': 'invisible'};
|
||||
scope.selected = 'regularProperty';
|
||||
scope.$digest();
|
||||
|
||||
expect(scope.createLabel).toHaveBeenCalledWith('regularProperty');
|
||||
expect(scope.createLabel).not.toHaveBeenCalledWith('$$private');
|
||||
expect(scope.createLabel).not.toHaveBeenCalledWith('$property');
|
||||
});
|
||||
|
||||
it('should allow expressions over multiple lines', function() {
|
||||
scope.isNotFoo = function(item) {
|
||||
return item.name !== 'Foo';
|
||||
@@ -1176,6 +1213,26 @@ describe('ngOptions', function() {
|
||||
|
||||
});
|
||||
|
||||
it('should not set view value again if the tracked property of the model has not changed when using trackBy', function() {
|
||||
|
||||
createSelect({
|
||||
'ng-model': 'selected',
|
||||
'ng-options': 'item for item in arr track by item.id'
|
||||
});
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.selected = {id: 10, label: 'ten'};
|
||||
});
|
||||
|
||||
spyOn(element.controller('ngModel'), '$setViewValue');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.arr[0] = {id: 10, label: 'ten'};
|
||||
});
|
||||
|
||||
expect(element.controller('ngModel').$setViewValue).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not re-render if a property of the model is changed when not using trackBy', function() {
|
||||
|
||||
createSelect({
|
||||
|
||||
@@ -23,7 +23,7 @@ describe('Filter: orderBy', function() {
|
||||
});
|
||||
|
||||
|
||||
it('shouldSortArrayInReverse', function() {
|
||||
it('should reverse collection if `reverseOrder` param is truthy', function() {
|
||||
expect(orderBy([{a:15}, {a:2}], 'a', true)).toEqualData([{a:15}, {a:2}]);
|
||||
expect(orderBy([{a:15}, {a:2}], 'a', "T")).toEqualData([{a:15}, {a:2}]);
|
||||
expect(orderBy([{a:15}, {a:2}], 'a', "reverse")).toEqualData([{a:15}, {a:2}]);
|
||||
@@ -116,7 +116,7 @@ describe('Filter: orderBy', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should not reverse array of objects with no predicate', function() {
|
||||
it('should not reverse array of objects with no predicate and reverse is not `true`', function() {
|
||||
var array = [
|
||||
{ id: 2 },
|
||||
{ id: 1 },
|
||||
@@ -126,6 +126,39 @@ describe('Filter: orderBy', function() {
|
||||
expect(orderBy(array)).toEqualData(array);
|
||||
});
|
||||
|
||||
it('should reverse array of objects with no predicate and reverse is `true`', function() {
|
||||
var array = [
|
||||
{ id: 2 },
|
||||
{ id: 1 },
|
||||
{ id: 4 },
|
||||
{ id: 3 }
|
||||
];
|
||||
var reversedArray = [
|
||||
{ id: 3 },
|
||||
{ id: 4 },
|
||||
{ id: 1 },
|
||||
{ id: 2 }
|
||||
];
|
||||
expect(orderBy(array, '', true)).toEqualData(reversedArray);
|
||||
});
|
||||
|
||||
|
||||
it('should reverse array of objects with predicate of "-"', function() {
|
||||
var array = [
|
||||
{ id: 2 },
|
||||
{ id: 1 },
|
||||
{ id: 4 },
|
||||
{ id: 3 }
|
||||
];
|
||||
var reversedArray = [
|
||||
{ id: 3 },
|
||||
{ id: 4 },
|
||||
{ id: 1 },
|
||||
{ id: 2 }
|
||||
];
|
||||
expect(orderBy(array, '-')).toEqualData(reversedArray);
|
||||
});
|
||||
|
||||
|
||||
it('should not reverse array of objects with null prototype and no predicate', function() {
|
||||
var array = [2,1,4,3].map(function(id) {
|
||||
@@ -151,6 +184,16 @@ describe('Filter: orderBy', function() {
|
||||
null
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
it('should sort array of arrays as Array.prototype.sort', function() {
|
||||
expect(orderBy([['one'], ['two'], ['three']])).toEqualData([['one'], ['three'], ['two']]);
|
||||
});
|
||||
|
||||
|
||||
it('should sort mixed array of objects and values in a stable way', function() {
|
||||
expect(orderBy([{foo: 2}, {foo: {}}, {foo: 3}, {foo: 4}], 'foo')).toEqualData([{foo: 2}, {foo: 3}, {foo: 4}, {foo: {}}]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
+1048
-735
File diff suppressed because it is too large
Load Diff
+7
-6
@@ -1,12 +1,6 @@
|
||||
/* global $LogProvider: false */
|
||||
'use strict';
|
||||
|
||||
function initService(debugEnabled) {
|
||||
return module(function($logProvider) {
|
||||
$logProvider.debugEnabled(debugEnabled);
|
||||
});
|
||||
}
|
||||
|
||||
describe('$log', function() {
|
||||
var $window, logger, log, warn, info, error, debug;
|
||||
|
||||
@@ -178,4 +172,11 @@ describe('$log', function() {
|
||||
expect(errorArgs).toEqual(['abc', 'message\nsourceURL:123']);
|
||||
});
|
||||
});
|
||||
|
||||
function initService(debugEnabled) {
|
||||
return module(function($logProvider) {
|
||||
$logProvider.debugEnabled(debugEnabled);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -1745,6 +1745,16 @@ describe('parser', function() {
|
||||
expect(scope.$eval("0&&2")).toEqual(0 && 2);
|
||||
expect(scope.$eval("0||2")).toEqual(0 || 2);
|
||||
expect(scope.$eval("0||1&&2")).toEqual(0 || 1 && 2);
|
||||
expect(scope.$eval("true&&a")).toEqual(true && undefined);
|
||||
expect(scope.$eval("true&&a()")).toEqual(true && undefined);
|
||||
expect(scope.$eval("true&&a()()")).toEqual(true && undefined);
|
||||
expect(scope.$eval("true&&a.b")).toEqual(true && undefined);
|
||||
expect(scope.$eval("true&&a.b.c")).toEqual(true && undefined);
|
||||
expect(scope.$eval("false||a")).toEqual(false || undefined);
|
||||
expect(scope.$eval("false||a()")).toEqual(false || undefined);
|
||||
expect(scope.$eval("false||a()()")).toEqual(false || undefined);
|
||||
expect(scope.$eval("false||a.b")).toEqual(false || undefined);
|
||||
expect(scope.$eval("false||a.b.c")).toEqual(false || undefined);
|
||||
});
|
||||
|
||||
it('should parse ternary', function() {
|
||||
|
||||
@@ -1643,6 +1643,14 @@ describe('q', function() {
|
||||
});
|
||||
|
||||
|
||||
describe('resolve', function() {
|
||||
it('should be an alias of the "when" function', function() {
|
||||
expect(q.resolve).toBeDefined();
|
||||
expect(q.resolve).toEqual(q.when);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('optional callbacks', function() {
|
||||
it('should not require success callback and propagate resolution', function() {
|
||||
q.when('hi', null, error()).then(success(2), error());
|
||||
|
||||
@@ -57,6 +57,31 @@ describe('$aria', function() {
|
||||
scope.$apply('val = true');
|
||||
expect(element.attr('aria-hidden')).toBe('userSetValue');
|
||||
});
|
||||
|
||||
it('should always set aria-hidden to a boolean value', function() {
|
||||
compileElement('<div ng-hide="val"></div>');
|
||||
|
||||
scope.$apply('val = "test angular"');
|
||||
expect(element.attr('aria-hidden')).toBe('true');
|
||||
|
||||
scope.$apply('val = null');
|
||||
expect(element.attr('aria-hidden')).toBe('false');
|
||||
|
||||
scope.$apply('val = {}');
|
||||
expect(element.attr('aria-hidden')).toBe('true');
|
||||
|
||||
|
||||
compileElement('<div ng-show="val"></div>');
|
||||
|
||||
scope.$apply('val = "test angular"');
|
||||
expect(element.attr('aria-hidden')).toBe('false');
|
||||
|
||||
scope.$apply('val = null');
|
||||
expect(element.attr('aria-hidden')).toBe('true');
|
||||
|
||||
scope.$apply('val = {}');
|
||||
expect(element.attr('aria-hidden')).toBe('false');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -319,6 +344,20 @@ describe('$aria', function() {
|
||||
scope.$apply('val = true');
|
||||
expectAriaAttrOnEachElement(element, 'aria-disabled', 'userSetValue');
|
||||
});
|
||||
|
||||
|
||||
it('should always set aria-disabled to a boolean value', function() {
|
||||
compileElement('<div ng-disabled="val"></div>');
|
||||
|
||||
scope.$apply('val = "test angular"');
|
||||
expect(element.attr('aria-disabled')).toBe('true');
|
||||
|
||||
scope.$apply('val = null');
|
||||
expect(element.attr('aria-disabled')).toBe('false');
|
||||
|
||||
scope.$apply('val = {}');
|
||||
expect(element.attr('aria-disabled')).toBe('true');
|
||||
});
|
||||
});
|
||||
|
||||
describe('aria-disabled when disabled', function() {
|
||||
@@ -510,6 +549,36 @@ describe('$aria', function() {
|
||||
expectAriaAttrOnEachElement(element, 'aria-valuemin', 'userSetValue2');
|
||||
expectAriaAttrOnEachElement(element, 'aria-valuemax', 'userSetValue3');
|
||||
});
|
||||
|
||||
|
||||
it('should update `aria-valuemin/max` when `min/max` changes dynamically', function() {
|
||||
scope.$apply('min = 25; max = 75');
|
||||
compileElement('<input type="range" ng-model="val" min="{{min}}" max="{{max}}" />');
|
||||
|
||||
expect(element.attr('aria-valuemin')).toBe('25');
|
||||
expect(element.attr('aria-valuemax')).toBe('75');
|
||||
|
||||
scope.$apply('min = 0');
|
||||
expect(element.attr('aria-valuemin')).toBe('0');
|
||||
|
||||
scope.$apply('max = 100');
|
||||
expect(element.attr('aria-valuemax')).toBe('100');
|
||||
});
|
||||
|
||||
|
||||
it('should update `aria-valuemin/max` when `ng-min/ng-max` changes dynamically', function() {
|
||||
scope.$apply('min = 25; max = 75');
|
||||
compileElement('<input type="range" ng-model="val" ng-min="min" ng-max="max" />');
|
||||
|
||||
expect(element.attr('aria-valuemin')).toBe('25');
|
||||
expect(element.attr('aria-valuemax')).toBe('75');
|
||||
|
||||
scope.$apply('min = 0');
|
||||
expect(element.attr('aria-valuemin')).toBe('0');
|
||||
|
||||
scope.$apply('max = 100');
|
||||
expect(element.attr('aria-valuemax')).toBe('100');
|
||||
});
|
||||
});
|
||||
|
||||
describe('announcing ngMessages', function() {
|
||||
@@ -681,6 +750,18 @@ describe('$aria', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('actions when bindRoleForClick is set to false', function() {
|
||||
beforeEach(configAriaProvider({
|
||||
bindRoleForClick: false
|
||||
}));
|
||||
beforeEach(injectScopeAndCompiler);
|
||||
|
||||
it('should not add a button role', function() {
|
||||
compileElement('<radio-group ng-click="something"></radio-group>');
|
||||
expect(element.attr('role')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('actions when bindKeypress is set to false', function() {
|
||||
beforeEach(configAriaProvider({
|
||||
bindKeypress: false
|
||||
|
||||
@@ -20,6 +20,15 @@ describe('linky', function() {
|
||||
expect(linky(undefined)).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should be case-insensitive', function() {
|
||||
expect(linky('WWW.example.com')).toEqual('<a href="http://WWW.example.com">WWW.example.com</a>');
|
||||
expect(linky('WWW.EXAMPLE.COM')).toEqual('<a href="http://WWW.EXAMPLE.COM">WWW.EXAMPLE.COM</a>');
|
||||
expect(linky('HTTP://www.example.com')).toEqual('<a href="HTTP://www.example.com">HTTP://www.example.com</a>');
|
||||
expect(linky('HTTP://example.com')).toEqual('<a href="HTTP://example.com">HTTP://example.com</a>');
|
||||
expect(linky('HTTPS://www.example.com')).toEqual('<a href="HTTPS://www.example.com">HTTPS://www.example.com</a>');
|
||||
expect(linky('HTTPS://example.com')).toEqual('<a href="HTTPS://example.com">HTTPS://example.com</a>');
|
||||
});
|
||||
|
||||
it('should handle www.', function() {
|
||||
expect(linky('www.example.com')).toEqual('<a href="http://www.example.com">www.example.com</a>');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user