Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0524e92d2e | |||
| 9b96cea462 | |||
| c90ad96808 | |||
| 69f69db1e0 | |||
| 8e28bb4c2f | |||
| ab41e48493 | |||
| ef640cbc2a | |||
| 081fef60b2 | |||
| 99d1a438b6 | |||
| 6bd4292c24 | |||
| e0663312fb | |||
| 9ae0c01c2b | |||
| 79b3b8b686 | |||
| 1b740974f5 | |||
| b9bdbe615c | |||
| 6617b42bc7 | |||
| 8a907eb3ff | |||
| aac3c4aa63 | |||
| d162f152b8 | |||
| 4025883803 | |||
| c437d0a470 | |||
| 5d28d19623 | |||
| 7fd2dc11ca | |||
| 63db09753e | |||
| a097aa95b7 | |||
| b3dfb38359 | |||
| 3b5ba87797 | |||
| e50002baed | |||
| a8089166f5 | |||
| d8e3707860 | |||
| ca4df475e1 | |||
| 02c9dc6e16 |
@@ -11,6 +11,9 @@ env:
|
||||
- JOB=unit BROWSER_PROVIDER=saucelabs
|
||||
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=saucelabs
|
||||
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=saucelabs
|
||||
- JOB=unit BROWSER_PROVIDER=browserstack
|
||||
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack
|
||||
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack
|
||||
global:
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
|
||||
@@ -1,3 +1,101 @@
|
||||
<a name="1.3.6"></a>
|
||||
# 1.3.6 robofunky-danceblaster (2014-12-08)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$browser:** prevent infinite digests when clearing the hash of a url
|
||||
([10ac5948](https://github.com/angular/angular.js/commit/10ac5948097e2c8eaead238603d29ee580dc8273),
|
||||
[#9629](https://github.com/angular/angular.js/issues/9629), [#9635](https://github.com/angular/angular.js/issues/9635), [#10228](https://github.com/angular/angular.js/issues/10228), [#10308](https://github.com/angular/angular.js/issues/10308))
|
||||
- **$http:** preserve config object when resolving from cache
|
||||
([facfec98](https://github.com/angular/angular.js/commit/facfec98412c0bb8678d578bade05ffef06a9e84),
|
||||
[#9004](https://github.com/angular/angular.js/issues/9004), [#9030](https://github.com/angular/angular.js/issues/9030))
|
||||
- **$location:**
|
||||
- allow hash fragments with hashPrefix in hash-bang location urls
|
||||
([2dc34a96](https://github.com/angular/angular.js/commit/2dc34a969956eea680be4c8d9f800556d110996a),
|
||||
[#9629](https://github.com/angular/angular.js/issues/9629), [#9635](https://github.com/angular/angular.js/issues/9635), [#10228](https://github.com/angular/angular.js/issues/10228), [#10308](https://github.com/angular/angular.js/issues/10308))
|
||||
- strip off empty hash segments when comparing
|
||||
([e93710fe](https://github.com/angular/angular.js/commit/e93710fe0e4fb05ceee59a04f290692a5bec5d20),
|
||||
[#9635](https://github.com/angular/angular.js/issues/9635))
|
||||
- **$parse:**
|
||||
- fix operators associativity
|
||||
([ed1243ff](https://github.com/angular/angular.js/commit/ed1243ffc7c2cb4bd5b4dece597597db8eb08e34))
|
||||
- follow JavaScript context for unbound functions
|
||||
([429938da](https://github.com/angular/angular.js/commit/429938da1f45b8a649b8c77762fb0ae59b6d0cea))
|
||||
- **filterFilter:**
|
||||
- don't match primitive sub-expressions against any prop
|
||||
([a75537d4](https://github.com/angular/angular.js/commit/a75537d461c92e3455e372ff5005bf0cad2d2e95))
|
||||
- ignore function properties and account for inherited properties
|
||||
([5ced914c](https://github.com/angular/angular.js/commit/5ced914cc8625008e6249d5ac5942d5822287cc0),
|
||||
[#9984](https://github.com/angular/angular.js/issues/9984))
|
||||
- correctly handle deep expression objects
|
||||
([f7cf8460](https://github.com/angular/angular.js/commit/f7cf846045b1e2fb39c62e304c61b44d5c805e31),
|
||||
[#7323](https://github.com/angular/angular.js/issues/7323), [#9698](https://github.com/angular/angular.js/issues/9698), [#9757](https://github.com/angular/angular.js/issues/9757))
|
||||
- **inputs:** ignoring input events in IE caused by placeholder changes or focus/blur on inputs with placeholders
|
||||
([55d9db56](https://github.com/angular/angular.js/commit/55d9db56a6f7d29b16f8393612648080c6d535d6),
|
||||
[#9265](https://github.com/angular/angular.js/issues/9265))
|
||||
- **linky:** make urls starting with www. links, like markdown
|
||||
([915a891a](https://github.com/angular/angular.js/commit/915a891ad4cdcaa5e47e976db8f4d402d230be77),
|
||||
[#10290](https://github.com/angular/angular.js/issues/10290))
|
||||
- **ngAnimate:** do not use jQuery class API
|
||||
([40a537c2](https://github.com/angular/angular.js/commit/40a537c25f70ad556a41bb2d00ea3e257410e9af),
|
||||
[#10024](https://github.com/angular/angular.js/issues/10024), [#10329](https://github.com/angular/angular.js/issues/10329))
|
||||
- **ngMock:** allow numeric timeouts in $httpBackend mock
|
||||
([acb066e8](https://github.com/angular/angular.js/commit/acb066e84a10483e1025eed295352b66747dbb8a),
|
||||
[#4891](https://github.com/angular/angular.js/issues/4891))
|
||||
- **ngModel:**
|
||||
- always use the most recent viewValue for validation
|
||||
([2d6a0a1d](https://github.com/angular/angular.js/commit/2d6a0a1dc1e7125cab2e30244e35e97e11802843),
|
||||
[#10126](https://github.com/angular/angular.js/issues/10126), [#10299](https://github.com/angular/angular.js/issues/10299))
|
||||
- fixing many keys incorrectly marking inputs as dirty
|
||||
([d21dff21](https://github.com/angular/angular.js/commit/d21dff21ed8beb015ad911f11d57cceb56fc439f))
|
||||
- **ngSanitize:** exclude smart quotes at the end of the link
|
||||
([7c6be43e](https://github.com/angular/angular.js/commit/7c6be43e83590798cffef63d076fb79d5296fba2),
|
||||
[#7307](https://github.com/angular/angular.js/issues/7307))
|
||||
- **numberFilter:** numbers rounding to zero shouldn't be negative
|
||||
([96c61fe7](https://github.com/angular/angular.js/commit/96c61fe756d7d3db011818bf0925e3d86ffff8ce),
|
||||
[#10278](https://github.com/angular/angular.js/issues/10278))
|
||||
- **orderBy:**
|
||||
- make object-to-primtiive behaviour work for objects with null prototype
|
||||
([3aa57528](https://github.com/angular/angular.js/commit/3aa5752894419b4638d5c934879258fa6a1c0d07))
|
||||
- maintain order in array of objects when predicate is not provided
|
||||
([8bfeddb5](https://github.com/angular/angular.js/commit/8bfeddb5d671017f4a21b8b46334ac816710b143),
|
||||
[#9566](https://github.com/angular/angular.js/issues/9566), [#9747](https://github.com/angular/angular.js/issues/9747), [#10311](https://github.com/angular/angular.js/issues/10311))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$$jqLite:** export jqLite as a private service
|
||||
([f2e7f875](https://github.com/angular/angular.js/commit/f2e7f875e2ad4b271c4e72ebd3860f905132eed9))
|
||||
- **$injector:** print caller name in "unknown provider" errors (when available)
|
||||
([013b522c](https://github.com/angular/angular.js/commit/013b522c9e690665aecb0e0f656e4557a673ec09),
|
||||
[#8135](https://github.com/angular/angular.js/issues/8135), [#9721](https://github.com/angular/angular.js/issues/9721))
|
||||
- **jsonFilter:** add optional arg to define custom indentation
|
||||
([1191edba](https://github.com/angular/angular.js/commit/1191edba4eaa15f675fa4ed047949a150843971b),
|
||||
[#9771](https://github.com/angular/angular.js/issues/9771))
|
||||
- **ngAria:** bind keypress on ng-click w/ option
|
||||
([5481e2cf](https://github.com/angular/angular.js/commit/5481e2cfcd4d136a1c7f45cd4ce0fa1a8a15074d),
|
||||
[#10288](https://github.com/angular/angular.js/issues/10288))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$location:** due to [2dc34a96](https://github.com/angular/angular.js/commit/2dc34a969956eea680be4c8d9f800556d110996a),
|
||||
|
||||
|
||||
We no longer throw an `ihshprfx` error if the URL after the base path
|
||||
contains only a hash fragment. Previously, if the base URL was `http://abc.com/base/`
|
||||
and the hashPrefix is `!` then trying to parse `http://abc.com/base/#some-fragment`
|
||||
would have thrown an error. Now we simply assume it is a normal fragment and
|
||||
that the path is empty, resulting `$location.absUrl() === "http://abc.com/base/#!/#some-fragment"`.
|
||||
|
||||
This should not break any applications, but you can no longer rely on receiving the
|
||||
`ihshprfx` error for paths that have the syntax above. It is actually more similar
|
||||
to what currently happens for invalid extra paths anyway: If the base URL
|
||||
and hashPrfix are set up as above, then `http://abc.com/base/other/path` does not
|
||||
throw an error but just ignores the extra path: `http://abc.com/base`.
|
||||
|
||||
|
||||
<a name="1.3.5"></a>
|
||||
# 1.3.5 cybernetic-mercantilism (2014-12-01)
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<div>ngBind: <input type="radio" ng-model="benchmarkType" value="ngBind"></div>
|
||||
<div>ngBindOnce: <input type="radio" ng-model="benchmarkType" value="ngBindOnce"></div>
|
||||
<div>interpolation: <input type="radio" ng-model="benchmarkType" value="interpolation"></div>
|
||||
<div>attribute interpolation: <input type="radio" ng-model="benchmarkType" value="interpolationAttr"></div>
|
||||
<div>ngBind + fnInvocation: <input type="radio" ng-model="benchmarkType" value="ngBindFn"></div>
|
||||
<div>interpolation + fnInvocation: <input type="radio" ng-model="benchmarkType" value="interpolationFn"></div>
|
||||
<div>ngBind + filter: <input type="radio" ng-model="benchmarkType" value="ngBindFilter"></div>
|
||||
@@ -46,6 +47,12 @@
|
||||
<span ng-repeat="column in row">{{column.i}}:{{column.j}}|</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="interpolationAttr">
|
||||
<h2>attribute interpolation</h2>
|
||||
<div ng-repeat="row in data">
|
||||
<span ng-repeat="column in row" i="{{column.i}}" j="{{column.j}}">i,j attrs</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="ngBindFn">
|
||||
<h2>bindings with functions</h2>
|
||||
<div ng-repeat="row in data">
|
||||
|
||||
@@ -148,7 +148,7 @@ or JavaScript callbacks.
|
||||
{@link ngAnimate CSS-based animations}
|
||||
</td>
|
||||
<td>
|
||||
Follow ngAnimate’s CSS naming structure to reference CSS transitions / keyframe animations in AngularJS. Once defined the animation can be triggered by referencing the CSS class within the HTML template code.
|
||||
Follow ngAnimate’s CSS naming structure to reference CSS transitions / keyframe animations in AngularJS. Once defined, the animation can be triggered by referencing the CSS class within the HTML template code.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -156,7 +156,7 @@ or JavaScript callbacks.
|
||||
{@link ngAnimate JS-based animations}
|
||||
</td>
|
||||
<td>
|
||||
Use {@link angular.Module#animation module.animation()} to register a JavaScript animation. Once registered the animation can be triggered by referencing the CSS class within the HTML template code.
|
||||
Use {@link angular.Module#animation module.animation()} to register a JavaScript animation. Once registered, the animation can be triggered by referencing the CSS class within the HTML template code.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
@ngdoc error
|
||||
@name ng:test
|
||||
@fullName Testability Not Found
|
||||
@description
|
||||
|
||||
Angular's testability helper, getTestability, requires a root element to be
|
||||
passed in. This helps differentiate between different Angular apps on the same
|
||||
page. This error is thrown when no injector is found for root element. It is
|
||||
often because the root element is outside of the ng-app.
|
||||
@@ -200,7 +200,7 @@ code is present, and match the CSS class name on the element, then AngularJS wil
|
||||
## Class and ngClass animation hooks
|
||||
|
||||
AngularJS also pays attention to CSS class changes on elements by triggering the **add** and **remove** hooks.
|
||||
This means that if a CSS class is added to or removed from an element then an animation can be executed in between
|
||||
This means that if a CSS class is added to or removed from an element then an animation can be executed in between,
|
||||
before the CSS class addition or removal is finalized. (Keep in mind that AngularJS will only be
|
||||
able to capture class changes if an **expression** or the **ng-class** directive is used on the element.)
|
||||
|
||||
|
||||
@@ -174,6 +174,15 @@ For example, we could fix the example above by instead writing:
|
||||
</svg>
|
||||
```
|
||||
|
||||
If one wants to modify a camelcased attribute (SVG elements have valid camelcased attributes), such as `viewBox` on the `svg` element, one can use underscores to denote that the attribute to bind to is naturally camelcased.
|
||||
|
||||
For example, to bind to `viewBox`, we can write:
|
||||
|
||||
```html
|
||||
<svg ng-attr-view_box="{{viewBox}}">
|
||||
</svg>
|
||||
```
|
||||
|
||||
|
||||
## Creating Directives
|
||||
|
||||
|
||||
@@ -298,14 +298,16 @@ then the expression is not fulfilled and will remain watched.
|
||||
|
||||
### How to benefit from one-time binding
|
||||
|
||||
When interpolating text or attributes. If the expression, once set, will not change
|
||||
then it is a candidate for one-time expression.
|
||||
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>
|
||||
```
|
||||
|
||||
When using a directive with bidirectional binding and the parameters will not change
|
||||
When using a directive with bidirectional binding and the parameters will not change:
|
||||
|
||||
```js
|
||||
someModule.directive('someDirective', function() {
|
||||
@@ -324,7 +326,7 @@ someModule.directive('someDirective', function() {
|
||||
```
|
||||
|
||||
|
||||
When using a directive that takes an expression
|
||||
When using a directive that takes an expression:
|
||||
|
||||
```html
|
||||
<ul>
|
||||
|
||||
@@ -293,7 +293,7 @@ after last change.
|
||||
|
||||
Angular provides basic implementation for most common HTML5 {@link ng.directive:input input}
|
||||
types: ({@link input[text] text}, {@link input[number] number}, {@link input[url] url},
|
||||
{@link input[email] email}, {@link input[radio] radio}, {@link input[checkbox] checkbox}),
|
||||
{@link input[email] email}, {@link input[date] date}, {@link input[radio] radio}, {@link input[checkbox] checkbox}),
|
||||
as well as some directives for validation (`required`, `pattern`, `minlength`, `maxlength`,
|
||||
`min`, `max`).
|
||||
|
||||
|
||||
@@ -117,12 +117,12 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
### Courses
|
||||
* **Free online:**
|
||||
[thinkster.io](http://thinkster.io),
|
||||
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1)
|
||||
[CodeAcademy](http://www.codecademy.com/courses/javascript-advanced-en-2hJ3J/0/1),
|
||||
[CodeSchool](https://www.codeschool.com/courses/shaping-up-with-angular-js)
|
||||
* **Paid online:**
|
||||
[Pluralsite (3 courses)](http://www.pluralsight.com/training/Courses/Find?highlight=true&searchTerm=angularjs),
|
||||
[Tuts+](https://tutsplus.com/course/easier-js-apps-with-angular/),
|
||||
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html)
|
||||
[lynda.com](http://www.lynda.com/AngularJS-tutorials/Up-Running-AngularJS/133318-2.html),
|
||||
[WintellectNOW (4 lessons)](http://www.wintellectnow.com/Course/Detail/mastering-angularjs)
|
||||
* **Paid onsite:**
|
||||
[angularbootcamp.com](http://angularbootcamp.com/)
|
||||
|
||||
@@ -450,6 +450,45 @@ After:
|
||||
Please view the documentation for ngAnimate for more info.
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
- due to [85880a64](https://github.com/angular/angular.js/commit/85880a64900fa22a61feb926bf52de0965332ca5), some deprecated features of
|
||||
Protractor tests no longer work.
|
||||
|
||||
`by.binding(descriptor)` no longer allows using the surrounding interpolation
|
||||
markers in the descriptor (the default interpolation markers are `{{}}`).
|
||||
Previously, these were optional.
|
||||
|
||||
Before:
|
||||
|
||||
var el = element(by.binding('{{foo}}'));
|
||||
|
||||
After:
|
||||
|
||||
var el = element(by.binding('foo'));
|
||||
|
||||
Prefixes `ng_` and `x-ng-` are no longer allowed for models. Use `ng-model`.
|
||||
|
||||
`by.repeater` cannot find elements by row and column which are not children of
|
||||
the row. For example, if your template is
|
||||
|
||||
<div ng-repeat="foo in foos">{{foo.name}}</div>
|
||||
|
||||
Before:
|
||||
|
||||
var el = element(by.repeater('foo in foos').row(2).column('foo.name'))
|
||||
|
||||
After:
|
||||
|
||||
You may either enclose `{{foo.name}}` in a child element
|
||||
|
||||
<div ng-repeat="foo in foos"><span>{{foo.name}}</span></div>
|
||||
|
||||
or simply use:
|
||||
|
||||
var el = element(by.repeater('foo in foos').row(2))
|
||||
|
||||
|
||||
## Internet Explorer 8
|
||||
|
||||
- due to [eaa1d00b](https://github.com/angular/angular.js/commit/eaa1d00b24008f590b95ad099241b4003688cdda),
|
||||
|
||||
@@ -160,7 +160,7 @@ globally and run directly from a terminal/command prompt. You don't need to do t
|
||||
tutorial, but if you decide you do want to run them directly, you can install these modules globally
|
||||
using, `sudo npm install -g ...`.
|
||||
|
||||
For instance to install the Bower command line executable you would do:
|
||||
For instance, to install the Bower command line executable you would do:
|
||||
|
||||
```
|
||||
sudo npm install -g bower
|
||||
|
||||
@@ -16,9 +16,11 @@ function init {
|
||||
REPOS=(
|
||||
angular
|
||||
angular-animate
|
||||
angular-aria
|
||||
angular-cookies
|
||||
angular-i18n
|
||||
angular-loader
|
||||
angular-messages
|
||||
angular-mocks
|
||||
angular-route
|
||||
angular-resource
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"isWindow": false,
|
||||
"isScope": false,
|
||||
"isFile": false,
|
||||
"isFormData": false,
|
||||
"isBlob": false,
|
||||
"isBoolean": false,
|
||||
"isPromiseLike": false,
|
||||
|
||||
+12
-1
@@ -45,6 +45,7 @@
|
||||
isWindow: true,
|
||||
isScope: true,
|
||||
isFile: true,
|
||||
isFormData: true,
|
||||
isBlob: true,
|
||||
isBoolean: true,
|
||||
isPromiseLike: true,
|
||||
@@ -566,6 +567,11 @@ function isFile(obj) {
|
||||
}
|
||||
|
||||
|
||||
function isFormData(obj) {
|
||||
return toString.call(obj) === '[object FormData]';
|
||||
}
|
||||
|
||||
|
||||
function isBlob(obj) {
|
||||
return toString.call(obj) === '[object Blob]';
|
||||
}
|
||||
@@ -1428,7 +1434,12 @@ function reloadWithDebugInfo() {
|
||||
* @param {DOMElement} element DOM element which is the root of angular application.
|
||||
*/
|
||||
function getTestability(rootElement) {
|
||||
return angular.element(rootElement).injector().get('$$testability');
|
||||
var injector = angular.element(rootElement).injector();
|
||||
if (!injector) {
|
||||
throw ngMinErr('test',
|
||||
'no injector found for element argument to getTestability');
|
||||
}
|
||||
return injector.get('$$testability');
|
||||
}
|
||||
|
||||
var SNAKE_CASE_REGEXP = /[A-Z]/g;
|
||||
|
||||
+16
-10
@@ -1420,7 +1420,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
// support ngAttr attribute binding
|
||||
ngAttrName = directiveNormalize(name);
|
||||
if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
|
||||
name = snake_case(ngAttrName.substr(6), '-');
|
||||
name = name.replace(PREFIX_REGEXP, '')
|
||||
.substr(8).replace(/_(.)/g, function(match, letter) {
|
||||
return letter.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
|
||||
@@ -2332,7 +2335,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
|
||||
function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
|
||||
var interpolateFn = $interpolate(value, true);
|
||||
var trustedContext = getTrustedContext(node, name);
|
||||
allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
|
||||
|
||||
var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
|
||||
|
||||
// no interpolation found -> ignore
|
||||
if (!interpolateFn) return;
|
||||
@@ -2357,16 +2363,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
"ng- versions (such as ng-click instead of onclick) instead.");
|
||||
}
|
||||
|
||||
// If the attribute was removed, then we are done
|
||||
if (!attr[name]) {
|
||||
return;
|
||||
// If the attribute has changed since last $interpolate()ed
|
||||
var newValue = attr[name];
|
||||
if (newValue !== value) {
|
||||
// we need to interpolate again since the attribute value has been updated
|
||||
// (e.g. by another directive's compile function)
|
||||
// ensure unset/empty values make interpolateFn falsy
|
||||
interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
|
||||
value = newValue;
|
||||
}
|
||||
|
||||
// we need to interpolate again, in case the attribute value has been updated
|
||||
// (e.g. by another directive's compile function)
|
||||
interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name),
|
||||
ALL_OR_NOTHING_ATTRS[name] || allOrNothing);
|
||||
|
||||
// if attribute was updated so that there is no interpolation going on we don't want to
|
||||
// register any observers
|
||||
if (!interpolateFn) return;
|
||||
|
||||
@@ -257,7 +257,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||
var aliasAs = match[3];
|
||||
var trackByExp = match[4];
|
||||
|
||||
match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);
|
||||
match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
|
||||
|
||||
if (!match) {
|
||||
throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
|
||||
|
||||
@@ -356,8 +356,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d
|
||||
* * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
|
||||
* * `'a'`: AM/PM marker
|
||||
* * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
|
||||
* * `'ww'`: ISO-8601 week of year (00-53)
|
||||
* * `'w'`: ISO-8601 week of year (0-53)
|
||||
* * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
|
||||
* * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
|
||||
*
|
||||
* `format` string can also be one of the following predefined
|
||||
* {@link guide/i18n localizable formats}:
|
||||
|
||||
+27
-19
@@ -160,29 +160,37 @@ function orderByFilter($parse) {
|
||||
? function(a, b) {return comp(b,a);}
|
||||
: comp;
|
||||
}
|
||||
|
||||
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.toString === 'function') {
|
||||
value = value.toString();
|
||||
if (isPrimitive(value)) return value;
|
||||
}
|
||||
if (typeof value.valueOf === 'function') {
|
||||
value = value.valueOf();
|
||||
if (isPrimitive(value)) return value;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function compare(v1, v2) {
|
||||
var t1 = typeof v1;
|
||||
var t2 = typeof v2;
|
||||
// Prepare values for Abstract Relational Comparison
|
||||
// (http://www.ecma-international.org/ecma-262/5.1/#sec-11.8.5):
|
||||
// If the resulting values are identical, return 0 to prevent
|
||||
// incorrect re-ordering.
|
||||
if (t1 === t2 && t1 === "object") {
|
||||
// If types are both numbers, emulate abstract ToPrimitive() operation
|
||||
// in order to get primitive values suitable for comparison
|
||||
t1 = typeof (v1.valueOf ? v1 = v1.valueOf() : v1);
|
||||
t2 = typeof (v2.valueOf ? v2 = v2.valueOf() : v2);
|
||||
if (t1 === t2 && t1 === "object") {
|
||||
// Object.prototype.valueOf will return the original object, by
|
||||
// default. If we do not receive a primitive value, use ToString()
|
||||
// instead.
|
||||
t1 = typeof (v1.toString ? v1 = v1.toString() : v1);
|
||||
t2 = typeof (v2.toString ? v2 = v2.toString() : v2);
|
||||
|
||||
// If the end result of toString() for each item is the same, do not
|
||||
// perform relational comparison, and do not re-order objects.
|
||||
if (t1 === t2 && v1 === v2 || t1 === "object") return 0;
|
||||
}
|
||||
v1 = objectToString(v1);
|
||||
v2 = objectToString(v2);
|
||||
}
|
||||
if (t1 === t2) {
|
||||
if (t1 === "string") {
|
||||
|
||||
+59
-46
@@ -2,23 +2,34 @@
|
||||
|
||||
var APPLICATION_JSON = 'application/json';
|
||||
var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
|
||||
var JSON_START = /^\s*(\[|\{[^\{])/;
|
||||
var JSON_END = /[\}\]]\s*$/;
|
||||
var JSON_START = /^\[|^\{(?!\{)/;
|
||||
var JSON_ENDS = {
|
||||
'[': /]$/,
|
||||
'{': /}$/
|
||||
};
|
||||
var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
|
||||
|
||||
function defaultHttpResponseTransform(data, headers) {
|
||||
if (isString(data)) {
|
||||
// strip json vulnerability protection prefix
|
||||
data = data.replace(JSON_PROTECTION_PREFIX, '');
|
||||
var contentType = headers('Content-Type');
|
||||
if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0 && data.trim()) ||
|
||||
(JSON_START.test(data) && JSON_END.test(data))) {
|
||||
data = fromJson(data);
|
||||
// Strip json vulnerability protection prefix and trim whitespace
|
||||
var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
|
||||
|
||||
if (tempData) {
|
||||
var contentType = headers('Content-Type');
|
||||
if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
|
||||
data = fromJson(tempData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function isJsonLike(str) {
|
||||
var jsonStart = str.match(JSON_START);
|
||||
return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse headers into key value object
|
||||
*
|
||||
@@ -81,16 +92,17 @@ function headersGetter(headers) {
|
||||
* This function is used for both request and response transforming
|
||||
*
|
||||
* @param {*} data Data to transform.
|
||||
* @param {function(string=)} headers Http headers getter fn.
|
||||
* @param {function(string=)} headers HTTP headers getter fn.
|
||||
* @param {number} status HTTP status code of the response.
|
||||
* @param {(Function|Array.<Function>)} fns Function or an array of functions.
|
||||
* @returns {*} Transformed data.
|
||||
*/
|
||||
function transformData(data, headers, fns) {
|
||||
function transformData(data, headers, status, fns) {
|
||||
if (isFunction(fns))
|
||||
return fns(data, headers);
|
||||
return fns(data, headers, status);
|
||||
|
||||
forEach(fns, function(fn) {
|
||||
data = fn(data, headers);
|
||||
data = fn(data, headers, status);
|
||||
});
|
||||
|
||||
return data;
|
||||
@@ -142,7 +154,7 @@ function $HttpProvider() {
|
||||
|
||||
// transform outgoing request data
|
||||
transformRequest: [function(d) {
|
||||
return isObject(d) && !isFile(d) && !isBlob(d) ? toJson(d) : d;
|
||||
return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
|
||||
}],
|
||||
|
||||
// default headers
|
||||
@@ -369,7 +381,7 @@ function $HttpProvider() {
|
||||
*
|
||||
* Both requests and responses can be transformed using transformation functions: `transformRequest`
|
||||
* and `transformResponse`. These properties can be a single function that returns
|
||||
* the transformed value (`{function(data, headersGetter)`) or an array of such transformation functions,
|
||||
* the transformed value (`{function(data, headersGetter, status)`) or an array of such transformation functions,
|
||||
* which allows you to `push` or `unshift` a new transformation function into the transformation chain.
|
||||
*
|
||||
* ### Default Transformations
|
||||
@@ -613,9 +625,9 @@ function $HttpProvider() {
|
||||
* See {@link ng.$http#overriding-the-default-transformations-per-request
|
||||
* Overriding the Default Transformations}
|
||||
* - **transformResponse** –
|
||||
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
|
||||
* `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
|
||||
* transform function or an array of such functions. The transform function takes the http
|
||||
* response body and headers and returns its transformed (typically deserialized) version.
|
||||
* 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}
|
||||
* - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
|
||||
@@ -738,24 +750,23 @@ function $HttpProvider() {
|
||||
</example>
|
||||
*/
|
||||
function $http(requestConfig) {
|
||||
var config = {
|
||||
method: 'get',
|
||||
transformRequest: defaults.transformRequest,
|
||||
transformResponse: defaults.transformResponse
|
||||
};
|
||||
var headers = mergeHeaders(requestConfig);
|
||||
|
||||
if (!angular.isObject(requestConfig)) {
|
||||
throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
|
||||
}
|
||||
|
||||
extend(config, requestConfig);
|
||||
config.headers = headers;
|
||||
var config = extend({
|
||||
method: 'get',
|
||||
transformRequest: defaults.transformRequest,
|
||||
transformResponse: defaults.transformResponse
|
||||
}, requestConfig);
|
||||
|
||||
config.headers = mergeHeaders(requestConfig);
|
||||
config.method = uppercase(config.method);
|
||||
|
||||
var serverRequest = function(config) {
|
||||
headers = config.headers;
|
||||
var reqData = transformData(config.data, headersGetter(headers), config.transformRequest);
|
||||
var headers = config.headers;
|
||||
var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
|
||||
|
||||
// strip content-type if data is undefined
|
||||
if (isUndefined(reqData)) {
|
||||
@@ -771,7 +782,7 @@ function $HttpProvider() {
|
||||
}
|
||||
|
||||
// send request
|
||||
return sendReq(config, reqData, headers).then(transformResponse, transformResponse);
|
||||
return sendReq(config, reqData).then(transformResponse, transformResponse);
|
||||
};
|
||||
|
||||
var chain = [serverRequest, undefined];
|
||||
@@ -816,13 +827,30 @@ function $HttpProvider() {
|
||||
if (!response.data) {
|
||||
resp.data = response.data;
|
||||
} else {
|
||||
resp.data = transformData(response.data, response.headers, config.transformResponse);
|
||||
resp.data = transformData(response.data, response.headers, response.status, config.transformResponse);
|
||||
}
|
||||
return (isSuccess(response.status))
|
||||
? resp
|
||||
: $q.reject(resp);
|
||||
}
|
||||
|
||||
function executeHeaderFns(headers) {
|
||||
var headerContent, processedHeaders = {};
|
||||
|
||||
forEach(headers, function(headerFn, header) {
|
||||
if (isFunction(headerFn)) {
|
||||
headerContent = headerFn();
|
||||
if (headerContent != null) {
|
||||
processedHeaders[header] = headerContent;
|
||||
}
|
||||
} else {
|
||||
processedHeaders[header] = headerFn;
|
||||
}
|
||||
});
|
||||
|
||||
return processedHeaders;
|
||||
}
|
||||
|
||||
function mergeHeaders(config) {
|
||||
var defHeaders = defaults.headers,
|
||||
reqHeaders = extend({}, config.headers),
|
||||
@@ -845,23 +873,7 @@ function $HttpProvider() {
|
||||
}
|
||||
|
||||
// execute if header value is a function for merged headers
|
||||
execHeaders(reqHeaders);
|
||||
return reqHeaders;
|
||||
|
||||
function execHeaders(headers) {
|
||||
var headerContent;
|
||||
|
||||
forEach(headers, function(headerFn, header) {
|
||||
if (isFunction(headerFn)) {
|
||||
headerContent = headerFn();
|
||||
if (headerContent != null) {
|
||||
headers[header] = headerContent;
|
||||
} else {
|
||||
delete headers[header];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return executeHeaderFns(reqHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1004,11 +1016,12 @@ function $HttpProvider() {
|
||||
* !!! ACCESSES CLOSURE VARS:
|
||||
* $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
|
||||
*/
|
||||
function sendReq(config, reqData, reqHeaders) {
|
||||
function sendReq(config, reqData) {
|
||||
var deferred = $q.defer(),
|
||||
promise = deferred.promise,
|
||||
cache,
|
||||
cachedResp,
|
||||
reqHeaders = config.headers,
|
||||
url = buildUrl(config.url, config.params);
|
||||
|
||||
$http.pendingRequests.push(config);
|
||||
|
||||
+3
-3
@@ -788,8 +788,8 @@ function $LocationProvider() {
|
||||
* @param {string=} oldState History state object that was before it was changed.
|
||||
*/
|
||||
|
||||
this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
|
||||
function($rootScope, $browser, $sniffer, $rootElement) {
|
||||
this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
|
||||
function($rootScope, $browser, $sniffer, $rootElement, $window) {
|
||||
var $location,
|
||||
LocationMode,
|
||||
baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
|
||||
@@ -871,7 +871,7 @@ function $LocationProvider() {
|
||||
if ($location.absUrl() != $browser.url()) {
|
||||
$rootScope.$apply();
|
||||
// hack to work around FF6 bug 684208 when scenario runner clicks on links
|
||||
window.angular['ff-684208-preventDefault'] = true;
|
||||
$window.angular['ff-684208-preventDefault'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -362,6 +362,8 @@ Parser.prototype = {
|
||||
primary = this.arrayDeclaration();
|
||||
} else if (this.expect('{')) {
|
||||
primary = this.object();
|
||||
} else if (this.peek().identifier && this.peek().text in CONSTANTS) {
|
||||
primary = CONSTANTS[this.consume().text];
|
||||
} else if (this.peek().identifier) {
|
||||
primary = this.identifier();
|
||||
} else if (this.peek().constant) {
|
||||
@@ -464,7 +466,7 @@ Parser.prototype = {
|
||||
id += this.consume().text + this.consume().text;
|
||||
}
|
||||
|
||||
return CONSTANTS[id] || getterFn(id, this.options, this.text);
|
||||
return getterFn(id, this.options, this.text);
|
||||
},
|
||||
|
||||
constant: function() {
|
||||
@@ -654,17 +656,16 @@ Parser.prototype = {
|
||||
},
|
||||
|
||||
fieldAccess: function(object) {
|
||||
var expression = this.text;
|
||||
var field = this.consume().text;
|
||||
var getter = getterFn(field, this.options, expression);
|
||||
var getter = this.identifier();
|
||||
|
||||
return extend(function $parseFieldAccess(scope, locals, self) {
|
||||
return getter(self || object(scope, locals));
|
||||
var o = self || object(scope, locals);
|
||||
return (o == null) ? undefined : getter(o);
|
||||
}, {
|
||||
assign: function(scope, value, locals) {
|
||||
var o = object(scope, locals);
|
||||
if (!o) object.assign(scope, o = {});
|
||||
return setter(o, field, value, expression);
|
||||
return getter.assign(o, value);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
+1
-3
@@ -3,12 +3,10 @@
|
||||
function $$RAFProvider() { //rAF
|
||||
this.$get = ['$window', '$timeout', function($window, $timeout) {
|
||||
var requestAnimationFrame = $window.requestAnimationFrame ||
|
||||
$window.webkitRequestAnimationFrame ||
|
||||
$window.mozRequestAnimationFrame;
|
||||
$window.webkitRequestAnimationFrame;
|
||||
|
||||
var cancelAnimationFrame = $window.cancelAnimationFrame ||
|
||||
$window.webkitCancelAnimationFrame ||
|
||||
$window.mozCancelAnimationFrame ||
|
||||
$window.webkitCancelRequestAnimationFrame;
|
||||
|
||||
var rafSupported = !!requestAnimationFrame;
|
||||
|
||||
+4
-4
@@ -105,7 +105,6 @@ function $RootScopeProvider() {
|
||||
var child = parent.$new();
|
||||
|
||||
parent.salutation = "Hello";
|
||||
child.name = "World";
|
||||
expect(child.salutation).toEqual('Hello');
|
||||
|
||||
child.salutation = "Welcome";
|
||||
@@ -743,7 +742,7 @@ function $RootScopeProvider() {
|
||||
while (asyncQueue.length) {
|
||||
try {
|
||||
asyncTask = asyncQueue.shift();
|
||||
asyncTask.scope.$eval(asyncTask.expression);
|
||||
asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
}
|
||||
@@ -958,8 +957,9 @@ function $RootScopeProvider() {
|
||||
* - `string`: execute using the rules as defined in {@link guide/expression expression}.
|
||||
* - `function(scope)`: execute the function with the current `scope` parameter.
|
||||
*
|
||||
* @param {(object)=} locals Local variables object, useful for overriding values in scope.
|
||||
*/
|
||||
$evalAsync: function(expr) {
|
||||
$evalAsync: function(expr, locals) {
|
||||
// if we are outside of an $digest loop and this is the first time we are scheduling async
|
||||
// task also schedule async auto-flush
|
||||
if (!$rootScope.$$phase && !asyncQueue.length) {
|
||||
@@ -970,7 +970,7 @@ function $RootScopeProvider() {
|
||||
});
|
||||
}
|
||||
|
||||
asyncQueue.push({scope: this, expression: expr});
|
||||
asyncQueue.push({scope: this, expression: expr, locals: locals});
|
||||
},
|
||||
|
||||
$$postDigest: function(fn) {
|
||||
|
||||
@@ -111,7 +111,7 @@ function shallowClearAndCopy(src, dst) {
|
||||
* example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
|
||||
* will be `data.someProp`.
|
||||
*
|
||||
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend
|
||||
* @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
|
||||
* the default set of resource actions. The declaration should be created in the format of {@link
|
||||
* ng.$http#usage $http.config}:
|
||||
*
|
||||
|
||||
+36
-2
@@ -6542,16 +6542,21 @@ describe('$compile', function() {
|
||||
expect(element.attr('href')).toBe('test/test');
|
||||
}));
|
||||
|
||||
it('should work if they are prefixed with x- or data-', inject(function($compile, $rootScope) {
|
||||
it('should work if they are prefixed with x- or data- and different prefixes', inject(function($compile, $rootScope) {
|
||||
$rootScope.name = "Misko";
|
||||
element = $compile('<span data-ng-attr-test2="{{name}}" x-ng-attr-test3="{{name}}" data-ng:attr-test4="{{name}}"></span>')($rootScope);
|
||||
element = $compile('<span data-ng-attr-test2="{{name}}" x-ng-attr-test3="{{name}}" data-ng:attr-test4="{{name}}" ' +
|
||||
'x_ng-attr-test5="{{name}}" data:ng-attr-test6="{{name}}"></span>')($rootScope);
|
||||
expect(element.attr('test2')).toBeUndefined();
|
||||
expect(element.attr('test3')).toBeUndefined();
|
||||
expect(element.attr('test4')).toBeUndefined();
|
||||
expect(element.attr('test5')).toBeUndefined();
|
||||
expect(element.attr('test6')).toBeUndefined();
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('test2')).toBe('Misko');
|
||||
expect(element.attr('test3')).toBe('Misko');
|
||||
expect(element.attr('test4')).toBe('Misko');
|
||||
expect(element.attr('test5')).toBe('Misko');
|
||||
expect(element.attr('test6')).toBe('Misko');
|
||||
}));
|
||||
|
||||
describe('when an attribute has a dash-separated name', function() {
|
||||
@@ -6619,6 +6624,35 @@ describe('$compile', function() {
|
||||
});
|
||||
|
||||
|
||||
describe('when an attribute has an underscore-separated name', function() {
|
||||
|
||||
it('should work with different prefixes', inject(function($compile, $rootScope) {
|
||||
$rootScope.dimensions = "0 0 0 0";
|
||||
element = $compile('<svg ng:attr:view_box="{{dimensions}}"></svg>')($rootScope);
|
||||
expect(element.attr('viewBox')).toBeUndefined();
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('viewBox')).toBe('0 0 0 0');
|
||||
}));
|
||||
|
||||
it('should work if they are prefixed with x- or data-', inject(function($compile, $rootScope) {
|
||||
$rootScope.dimensions = "0 0 0 0";
|
||||
$rootScope.number = 0.42;
|
||||
$rootScope.scale = 1;
|
||||
element = $compile('<svg data-ng-attr-view_box="{{dimensions}}">' +
|
||||
'<filter x-ng-attr-filter_units="{{number}}">' +
|
||||
'<feDiffuseLighting data-ng:attr_surface_scale="{{scale}}">' +
|
||||
'</feDiffuseLighting>' +
|
||||
'<feSpecularLighting x-ng:attr_surface_scale="{{scale}}">' +
|
||||
'</feSpecularLighting></filter></svg>')($rootScope);
|
||||
expect(element.attr('viewBox')).toBeUndefined();
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('viewBox')).toBe('0 0 0 0');
|
||||
expect(element.find('filter').attr('filterUnits')).toBe('0.42');
|
||||
expect(element.find('feDiffuseLighting').attr('surfaceScale')).toBe('1');
|
||||
expect(element.find('feSpecularLighting').attr('surfaceScale')).toBe('1');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('multi-element directive', function() {
|
||||
it('should group on link function', inject(function($compile, $rootScope) {
|
||||
$rootScope.show = false;
|
||||
|
||||
@@ -146,6 +146,16 @@ describe('ngRepeat', function() {
|
||||
expect(element.text()).toEqual('misko:swe|shyam:set|');
|
||||
});
|
||||
|
||||
it('should iterate over on object/map where (key,value) contains whitespaces', function() {
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
'<li ng-repeat="( key , value ) in items">{{key}}:{{value}}|</li>' +
|
||||
'</ul>')(scope);
|
||||
scope.items = {me:'swe', you:'set'};
|
||||
scope.$digest();
|
||||
expect(element.text()).toEqual('me:swe|you:set|');
|
||||
});
|
||||
|
||||
it('should iterate over an object/map with identical values', function() {
|
||||
element = $compile(
|
||||
'<ul>' +
|
||||
|
||||
@@ -326,6 +326,36 @@ describe('filters', function() {
|
||||
toEqual('2010-09-03T06:35:08-0530');
|
||||
});
|
||||
|
||||
it('should correctly calculate week number', function() {
|
||||
function formatWeek(dateToFormat) {
|
||||
return date(new angular.mock.TzDate(+5, dateToFormat + 'T12:00:00.000Z'), 'ww (EEE)');
|
||||
}
|
||||
|
||||
expect(formatWeek('2007-01-01')).toEqual('01 (Mon)');
|
||||
expect(formatWeek('2007-12-31')).toEqual('53 (Mon)');
|
||||
|
||||
expect(formatWeek('2008-01-01')).toEqual('01 (Tue)');
|
||||
expect(formatWeek('2008-12-31')).toEqual('53 (Wed)');
|
||||
|
||||
expect(formatWeek('2014-01-01')).toEqual('01 (Wed)');
|
||||
expect(formatWeek('2014-12-31')).toEqual('53 (Wed)');
|
||||
|
||||
expect(formatWeek('2009-01-01')).toEqual('01 (Thu)');
|
||||
expect(formatWeek('2009-12-31')).toEqual('53 (Thu)');
|
||||
|
||||
expect(formatWeek('2010-01-01')).toEqual('00 (Fri)');
|
||||
expect(formatWeek('2010-12-31')).toEqual('52 (Fri)');
|
||||
|
||||
expect(formatWeek('2011-01-01')).toEqual('00 (Sat)');
|
||||
expect(formatWeek('2011-01-02')).toEqual('01 (Sun)');
|
||||
expect(formatWeek('2011-01-03')).toEqual('01 (Mon)');
|
||||
expect(formatWeek('2011-12-31')).toEqual('52 (Sat)');
|
||||
|
||||
expect(formatWeek('2012-01-01')).toEqual('01 (Sun)');
|
||||
expect(formatWeek('2012-01-02')).toEqual('01 (Mon)');
|
||||
expect(formatWeek('2012-12-31')).toEqual('53 (Mon)');
|
||||
});
|
||||
|
||||
it('should treat single quoted strings as string literals', function() {
|
||||
expect(date(midnight, "yyyy'de' 'a'x'dd' 'adZ' h=H:m:saZ")).
|
||||
toEqual('2010de axdd adZ 12=0:5:8AM-0500');
|
||||
|
||||
@@ -125,6 +125,22 @@ describe('Filter: orderBy', function() {
|
||||
});
|
||||
expect(orderBy(array)).toEqualData(array);
|
||||
});
|
||||
|
||||
|
||||
it('should sort nulls as Array.prototype.sort', function() {
|
||||
var array = [
|
||||
{ id: 2 },
|
||||
null,
|
||||
{ id: 3 },
|
||||
null
|
||||
];
|
||||
expect(orderBy(array)).toEqualData([
|
||||
{ id: 2 },
|
||||
{ id: 3 },
|
||||
null,
|
||||
null
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -252,5 +268,21 @@ describe('Filter: orderBy', function() {
|
||||
});
|
||||
expect(orderBy(array)).toEqualData(array);
|
||||
});
|
||||
|
||||
|
||||
it('should sort nulls as Array.prototype.sort', function() {
|
||||
var array = [
|
||||
{ id: 2 },
|
||||
null,
|
||||
{ id: 3 },
|
||||
null
|
||||
];
|
||||
expect(orderBy(array)).toEqualData([
|
||||
{ id: 2 },
|
||||
{ id: 3 },
|
||||
null,
|
||||
null
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -991,6 +991,15 @@ describe('$http', function() {
|
||||
$http({ method: 'POST', url: '/url', data: blob });
|
||||
});
|
||||
|
||||
it('should ignore FormData objects', function() {
|
||||
if (!window.FormData) return;
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append('angular', 'is great');
|
||||
|
||||
$httpBackend.expect('POST', '/url', '[object FormData]').respond('');
|
||||
$http({ method: 'POST', url: '/url', data: formData });
|
||||
});
|
||||
|
||||
it('should have access to request headers', function() {
|
||||
$httpBackend.expect('POST', '/url', 'header1').respond(200);
|
||||
@@ -1046,6 +1055,16 @@ describe('$http', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should ignore leading/trailing whitespace', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(' \n {"foo":"bar","baz":23} \r\n \n ');
|
||||
$http({method: 'GET', url: '/url'}).success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
expect(callback.mostRecentCall.args[0]).toEqual({foo: 'bar', baz: 23});
|
||||
});
|
||||
|
||||
|
||||
it('should deserialize json numbers when response header contains application/json',
|
||||
function() {
|
||||
$httpBackend.expect('GET', '/url').respond('123', {'Content-Type': 'application/json'});
|
||||
@@ -1132,6 +1151,16 @@ describe('$http', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should retain security prefix if response is not json', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(')]}\',\n This is not JSON !');
|
||||
$http({method: 'GET', url: '/url'}).success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
expect(callback.mostRecentCall.args[0]).toEqual(')]}\',\n This is not JSON !');
|
||||
});
|
||||
|
||||
|
||||
it('should not attempt to deserialize json when HEAD request', function() {
|
||||
//per http spec for Content-Type, HEAD request should return a Content-Type header
|
||||
//set to what the content type would have been if a get was sent
|
||||
@@ -1173,6 +1202,20 @@ describe('$http', function() {
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
expect(callback.mostRecentCall.args[0]).toEqual('{{some}}');
|
||||
});
|
||||
|
||||
it('should not deserialize json when the opening and closing brackets do not match',
|
||||
function() {
|
||||
$httpBackend.expect('GET', '/url1').respond('[Code](url): function() {}');
|
||||
$httpBackend.expect('GET', '/url2').respond('{"is": "not"} ["json"]');
|
||||
$http.get('/url1').success(callback);
|
||||
$http.get('/url2').success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback.calls.length).toBe(2);
|
||||
expect(callback.calls[0].args[0]).toEqual('[Code](url): function() {}');
|
||||
expect(callback.calls[1].args[0]).toEqual('{"is": "not"} ["json"]');
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1189,6 +1232,19 @@ describe('$http', function() {
|
||||
expect(callback.mostRecentCall.args[0]).toBe('header1');
|
||||
});
|
||||
|
||||
it('should have access to response status', function() {
|
||||
$httpBackend.expect('GET', '/url').respond(200, 'response', {h1: 'header1'});
|
||||
$http.get('/url', {
|
||||
transformResponse: function(data, headers, status) {
|
||||
return status;
|
||||
}
|
||||
}).success(callback);
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(callback).toHaveBeenCalledOnce();
|
||||
expect(callback.mostRecentCall.args[0]).toBe(200);
|
||||
});
|
||||
|
||||
|
||||
it('should pipeline more functions', function() {
|
||||
function first(d, h) {return d + '-first' + ':' + h('h1');}
|
||||
|
||||
+14
-1
@@ -771,7 +771,7 @@ describe('parser', function() {
|
||||
scope.$eval('{}.toString.constructor.a = 1');
|
||||
}).toThrowMinErr(
|
||||
'$parse', 'isecfn','Referencing Function in Angular expressions is disallowed! ' +
|
||||
'Expression: {}.toString.constructor.a = 1');
|
||||
'Expression: toString.constructor.a');
|
||||
|
||||
expect(function() {
|
||||
scope.$eval('{}.toString["constructor"]["constructor"] = 1');
|
||||
@@ -1835,6 +1835,19 @@ describe('parser', function() {
|
||||
$rootScope.fn = function() {};
|
||||
expect($rootScope.$eval('foo + "bar" + fn()')).toBe('bar');
|
||||
}));
|
||||
|
||||
it('should treat properties named null/undefined as normal properties', inject(function($rootScope) {
|
||||
expect($rootScope.$eval("a.null.undefined.b", {a:{null:{undefined:{b: 1}}}})).toBe(1);
|
||||
}));
|
||||
|
||||
it('should not allow overriding null/undefined keywords', inject(function($rootScope) {
|
||||
expect($rootScope.$eval('null.a', {null: {a: 42}})).toBeUndefined();
|
||||
}));
|
||||
|
||||
it('should allow accessing null/undefined properties on `this`', inject(function($rootScope) {
|
||||
$rootScope.null = {a: 42};
|
||||
expect($rootScope.$eval('this.null.a')).toBe(42);
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1243,6 +1243,13 @@ describe('Scope', function() {
|
||||
expect($rootScope.log).toBe('12');
|
||||
}));
|
||||
|
||||
it('should allow passing locals to the expression', inject(function($rootScope) {
|
||||
$rootScope.log = '';
|
||||
$rootScope.$evalAsync('log = log + a', {a: 1});
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.log).toBe('1');
|
||||
}));
|
||||
|
||||
it('should run async expressions in their proper context', inject(function($rootScope) {
|
||||
var child = $rootScope.$new();
|
||||
$rootScope.ctx = 'root context';
|
||||
|
||||
Reference in New Issue
Block a user