Compare commits

...

32 Commits

Author SHA1 Message Date
Julie Ralph 0524e92d2e docs(migration): add end to end upgrade info to migration doc
There are a couple of changes to some Protractor tests that need to be made
when migrating from AngularJS 1.2 to 1.3 - document these in the migration
guide.

See https://github.com/angular/protractor/issues/1480

Closes #10377
2014-12-15 13:46:21 +00:00
Shahar Talmi 9b96cea462 feat($rootScope): allow passing locals argument to $evalAsync
Closes #10390
2014-12-15 13:45:12 +00:00
Jason Bedard c90ad96808 fix($parse): a chain of field accessors should use a single getterFn 2014-12-15 13:03:05 +01:00
Lucas Galfaso 69f69db1e0 revert($compile): use createMap() for $$observe listeners when initialized from attr interpolation
This reverts commit 8e28bb4c2f.
2014-12-14 13:32:55 +01:00
Jason Bedard 8e28bb4c2f fix($compile): use createMap() for $$observe listeners when initialized from attr interpolation 2014-12-14 12:41:04 +01:00
Pawel Kozlowski ab41e48493 docs(dateFilter): fix docs to match implementation for week no formatting
The existing documentation claims that dateFilter determines week no
according to the ISO8601 standard, but this is not the case as illustrated
by tests in this PR. More specifically, the implementation deviates from
ISO8601 in 2 important aspects:
- impl assumes Sun to be the first day of a week, ISO8601 mandates Mon
- impl allows weeks 0 (for years starting on Fri, Sat) while ISO8601
would mark them as a week 52/53 of a previous year.

Fixes #10314
Closes #10313

Closes #10445
2014-12-14 11:28:46 +01:00
Sekib Omazic ef640cbc2a fix(ngRepeat): allow extra whitespaces in (key,value) part of micro-syntax
e.g. (  aaa  ,   bbb  ) will be accepted by parser

Fixes #6827
Closes #6833
2014-12-14 10:22:38 +01:00
Ruben Vicario Gonzalez 081fef60b2 docs(tutorial/index): improve punctuation
Closes #10449
2014-12-14 09:50:28 +01:00
Wes 99d1a438b6 docs(Courses): fix syntax issue in developer guide
The courses section should use commas between links to differentiate the instances of each link

Closes #10448
2014-12-13 21:27:18 -05:00
Dan Tennery-Spalding 6bd4292c24 docs(API Reference): corrected two typos - two missing commas
In the ngAnimate section, there were two commas missing from two sentences. This is inconsistent with the grammar used in the rest of the API documentation and made the document (slightly) more difficult to read. The two sentences are shown below, with the new commas added:

1. "Once defined, the animation can be triggered"
                           ^
                    comma added

2. "Once registered, the animation can be triggered"
                              ^
                    comma added

Closes #10447
2014-12-13 20:17:54 -05:00
Aleksandar Djindjic e0663312fb docs($rootScope): clean up inheritance example
Closes #10441
2014-12-13 18:06:50 +01:00
Jason Bedard 9ae0c01c2b perf($compile): only re-$interpolate attribute values at link time if changed since compile 2014-12-13 12:56:39 +01:00
Jon Hoguet 79b3b8b686 chore($location): use $window instead of window
Fix one place were there was a reference to `window` and not to `$window`
2014-12-13 12:52:42 +01:00
Pawel Kozlowski 1b740974f5 feat($http): pass response status code to data transform functions
Fixes #10324
Closes #6734

Closes #10440
2014-12-12 19:51:34 +01:00
Georgios Kalpakas b9bdbe615c fix($http): only parse as JSON when opening/closing brackets match
Previously, due to weak JSON-detecting RegExp, string like `[...}` and
`{...]` would be considered JSON (even if they obviously aren't) and an
expection would be thrown while trying to parse them.

This commit makes sure the opening and closing brackets match. This
doesn't completely eliminate false positives (e.g. `[]{}[]`), but does
help reduce them.

Closes #10349
Closes #10357
2014-12-12 18:46:00 +01:00
Stefan 6617b42bc7 docs(guide/Forms): added link to input type "date"
Closes #10415
2014-12-11 21:19:32 +01:00
Vojta Jina 8a907eb3ff chore(travis): run the build twice (BS and SL)
Temporarily run the each job twice:
- using BrowserStack
- using SauceLabs
2014-12-11 09:52:00 -08:00
Rouven Weßling aac3c4aa63 chore($$raf) remove moz prefix for requestAnimationFrame
This drops support for Firefox 22 and older.

The moz-prefix is still supported, but there is an effort to drop it eventually:
https://bugzilla.mozilla.org/show_bug.cgi?id=909154

Closes #9577
2014-12-10 16:01:02 -05:00
Pawel Kozlowski d162f152b8 refactor($http): avoid re-creating execHeaders function
The execHeaders function was being re-defined inside mergeHeaders
function. Additionally it was mutating its arguments.

Closes #10359
2014-12-10 20:57:28 +01:00
Rouven Weßling 4025883803 fix($http): don't convert FormData objects to JSON
This won't enable FormData uploads in itself, as the Content-Type is automatically set to application/json.

Closes #10373
2014-12-10 19:06:07 +01:00
thorn0 c437d0a470 docs(CHANGELOG.md): better group changes in 1.3.6
Closes #10392
2014-12-10 19:04:00 +01:00
Danny Callaghan 5d28d19623 docs(guide/Animations): fix punctuation
Closes #10398
2014-12-10 19:01:42 +01:00
Caitlin Potter 7fd2dc11ca chore(bower/unpublish.sh): add angular-messages and angular-aria
The unpublish script was not set to unpublish those packages

Closes #10379
2014-12-09 16:20:06 -05:00
Julie Ralph 63db09753e style(testability): throw a more informative error when getting testability
The angular.getTestability method requires an element parameter to determine
which Angular application to use. Currently, if the element provided is
undefined or outside of an Angular app, the error message is 'cannot read
property get of undefined'. Improving to a more relevant error message.
2014-12-09 11:48:38 -08:00
Caitlin Potter a097aa95b7 fix(orderBy): do not try to call valueOf/toString on null
8bfeddb5d6 added changes to make relational operator work as it
normally would in JS --- unfortunately, this broke due to my failure to account for typeof null
being "object".

This refactoring attempts to convert object values to primitives still, in a fashion similar to
the SortCompare (and subsequently the ToString() algorithm) from ES, in order to account for `null`
and also simplify code to some degree.

BREAKING CHANGE:

Previously, if either value being compared in the orderBy comparator was null or undefined, the
order would not change. Now, this order behaves more like Array.prototype.sort, which by default
pushes `null` behind objects, due to `n` occurring after `[` (the first characters of their
stringified forms) in ASCII / Unicode. If `toString` is customized, or does not exist, the
behaviour is undefined.

Closes #10385
Closes #10386
2014-12-09 13:34:27 -05:00
Pawel Kozlowski b3dfb38359 refactor($http): avoid using closure vars in serverRequest fn
Closes #10361
2014-12-09 19:24:52 +01:00
Pawel Kozlowski 3b5ba87797 refactor($http): drop superfluous argument to sendReq
Closes #10360
2014-12-09 18:50:17 +01:00
Grzegorz Marzencki e50002baed docs($resource): fix typo
Closes #10383
2014-12-09 18:12:22 +01:00
Brian Scoles a8089166f5 docs(guide/Expressions): fix grammar, flow and punctuation
Closes #10384
2014-12-09 18:10:08 +01:00
Wesley Cho d8e3707860 feat($compile): add support for ng-attr with camelCased attributes
SVG attributes are case sensitive and some have upper case letters in them
This change ensures that we can identify these, when being used with the `ng-attr`
directive, by encoding upper case letters with a preceding underscore.

For example to apply `ng-attr` to the `viewBox` attribute we could write
`ng-attr-view_box` - or any of the other variants: `ng:attr:view_box`,
`data-ng-attr-view_box`, etc.

Closes #9845
Closes #10194
2014-12-09 11:34:27 +00:00
Caitlin Potter ca4df475e1 docs(CHANGELOG.md): remove the closes ... from 2dc34a96 notes 2014-12-08 19:12:15 -05:00
Caitlin Potter 02c9dc6e16 docs(CHANGELOG): add v1.3.6 changes
Closes #10376
2014-12-08 19:10:34 -05:00
32 changed files with 499 additions and 110 deletions
+3
View File
@@ -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
+98
View File
@@ -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)
+7
View File
@@ -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">
+2 -2
View File
@@ -148,7 +148,7 @@ or JavaScript callbacks.
{@link ngAnimate CSS-based animations}
</td>
<td>
Follow ngAnimates 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 ngAnimates 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>
+9
View File
@@ -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.
+1 -1
View File
@@ -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.)
+9
View File
@@ -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
+6 -4
View File
@@ -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>
+1 -1
View File
@@ -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`).
+2 -2
View File
@@ -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/)
+39
View File
@@ -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),
+1 -1
View File
@@ -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
+2
View File
@@ -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
+1
View File
@@ -51,6 +51,7 @@
"isWindow": false,
"isScope": false,
"isFile": false,
"isFormData": false,
"isBlob": false,
"isBoolean": false,
"isPromiseLike": false,
+12 -1
View File
@@ -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
View File
@@ -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;
+1 -1
View File
@@ -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}'.",
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
+1 -1
View File
@@ -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
View File
@@ -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;
+10
View File
@@ -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>' +
+30
View File
@@ -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');
+32
View File
@@ -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
]);
});
});
});
+56
View File
@@ -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
View File
@@ -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);
}));
});
});
});
+7
View File
@@ -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';