Compare commits

...

52 Commits

Author SHA1 Message Date
Jeff Cross d396f42537 docs(CHANGELOG.md): add upcoming breaking change 2014-09-09 16:21:16 -07:00
Jeff Cross 5ec900a125 docs(CHANGELOG.md): fix wording and remove reverted change 2014-09-09 16:20:20 -07:00
Jeff Cross 66503a6e18 docs(CHANGELOG.md): add changes for 1.3.0-rc.1 and 1.2.24 2014-09-09 15:48:30 -07:00
Jeff Cross 2d8749e8c9 revert: "fix($compile): render nested transclusion at the root of a template"
This reverts commit 9d9cdfb575.

This commit was causing breakages because of its assumption that transcluded
content would be handled predictably, i.e. with ngTransclude, whereas many
use cases involve manipulating transcluded content in linking functions.
2014-09-09 11:08:39 -07:00
Chirayu Krishnappa b39e1d47b9 fix($parse): disallow passing Function to Array.sort
Fix the following exploit:

    hasOwnProperty.constructor.prototype.valueOf = valueOf.call;
    ["a", "alert(1)"].sort(hasOwnProperty.constructor);

The exploit:
• 1. Array.sort takes a comparison function and passes it 2 parameters to compare.
  2. It then calls .valueOf() if the result is not a primitive.
• The Function object conveniently accepts two string arguments so we can use this
  to construct a function.  However, this doesn't do much unless we can execute it.
• We set the valueOf function on Function.prototype to Function.prototype.call.
  This causes the function that we constructed to be executed when sort calls
  .valueOf() on the result of the comparison.

The fix is in two parts.
• Disallow passing unsafe objects to function calls as parameters.
• Do not traverse the Function object when setting a path.
2014-09-09 10:46:36 -07:00
Peter Bacon Darwin 5061d2c97c chore(npm-shrinkwrap): safely update karma to 0.12.23 2014-09-09 10:21:45 +01:00
Peter Bacon Darwin a961291aa2 chore(npm-shrinkwrap): fix karma dependencies
Updating to karma 0.12.13 (in commit 408508ad29)
caused `iit` and `ddescribe` to crash and disconnect the browser stopping the
test run.

It appears that the problem is with one of the dependencies of karma rather
than karma itself. At least one of the karma dependencies updated in line
with karma's dependencies' semver specifications but subtly changed their
behaviour to break karma.  Possibly this is related to chokidar, glob,
minimatch or fsevents.
2014-09-09 10:04:32 +01:00
Peter Bacon Darwin 3068d8e52a docs($filter): remove duplicate documentation of register method 2014-09-08 14:31:47 +01:00
Peter Bacon Darwin d2dd3581a9 docs(form): move param tag outside of main description 2014-09-08 14:31:47 +01:00
Peter Bacon Darwin 53feb272be chore(bower): refactor bower usage
The gulp bower task in the docs app was never actually running since it couldn't
find the bower.json file and was silently failing. Updating to a newer bower
highlighted this issue.

This commit moves the docs app specific bower components into the docs folder.
There are only jquery and closure compiler related components in the project
folder now.

It also improves the gulp bower task to provide better feedback of progress
and errors.
2014-09-08 14:31:47 +01:00
Peter Bacon Darwin 408508ad29 chore(package.json): tidy up dependencies
Sorted dependencies into alphabetic order. If we can keep them like this
it will be much easier to keep track of version changes.

Updated bower and gulp to newer versions.
2014-09-08 14:31:47 +01:00
Peter Bacon Darwin ece7d19115 chore(clean-shrinkwrap): chokidar is fixed since 0.8.2 2014-09-08 12:31:40 +01:00
Pawel Kozlowski 992101da10 refactor($http): simplify buildUrl function
Closes #8955
2014-09-05 20:22:20 -04:00
Peter Bacon Darwin 47f42ecf45 test(orderBy): remove IE8 incompatible test code 2014-09-05 19:46:40 +01:00
Julie Ralph fd995abc9a chore(tests): fix up test for protractor 1.2.0 update
These changes were made to master in 85880a6490
but never made it to the 1.2.x branch.
2014-09-05 11:16:18 -07:00
Julie Ralph 5f9a9747d2 chore(tests): update Protractor to v1.2.0 2014-09-05 10:51:31 -07:00
Vitali Tsevan 94b0f2d35d fix(orderBy): allow arrayLike objects to be ordered
Closes #8944
2014-09-05 11:56:41 +01:00
Jeff Cross c12e8d4665 fix($location): don't call toString on null values 2014-09-04 17:46:51 -07:00
thorn0 c65796d496 fix($location): remove an unused parameter of $location.url 2014-09-04 15:12:04 -07:00
John Reilly 34b43eab5f docs($location): update search description to include number param 2014-09-04 14:55:48 -07:00
Peter Bacon Darwin 9d9cdfb575 fix($compile): render nested transclusion at the root of a template
Closes #8914
Closes #8925
2014-09-04 19:33:03 +01:00
Pawel Kozlowski 68a09ba74d fix($location): allow numeric location setter arguments
Fixes #7054
2014-09-04 10:47:30 -07:00
Peter Bacon Darwin 239d0b1f49 docs(guide): add sortOrder to each page
Finally we can control the order of the guide pages
2014-09-04 17:15:26 +01:00
Peter Bacon Darwin 4e04c73cd3 chore(docs): enable page ordering by @sortOrder tag 2014-09-04 16:49:55 +01:00
Peter Bacon Darwin 6c863e5bba chore(docs): improve searching by member
The keywords processor now also extracts the members (i.e. method, properties
and events) into its own search term property. These are then used in the lunr
search index with higher weighting that normal keywords to push services that
contain the query term as a member higher up the search results.

Closes #7661
2014-09-04 14:23:29 +01:00
Peter Bacon Darwin e0cf7c5bf2 Revert "fix($compile): render nested transclusion at the root of a template"
This reverts commit 466320f691.
2014-09-04 14:15:38 +01:00
Peter Bacon Darwin 466320f691 fix($compile): render nested transclusion at the root of a template
Closes #8914
Closes #8925
2014-09-04 13:45:49 +01:00
Smitha Milli 7e02fa07eb fix(numberFilter): format numbers that round to zero as nonnegative
Previously when a negative number was rounded to 0 by the number filter
it would be formated as a negative number.  This means something like
{{ -0.01 | number: 1 }} would output -0.0.  Now it will ouput 0.0
instead.

Closes #8489
2014-09-03 15:42:07 -07:00
thorn0 9ed9777317 docs(docs.css): improve comma spacing in TOC on mobile 2014-09-03 14:37:43 -07:00
Juampy e661bc9f15 docs($sce:unsafe): fix link to $sce docs
The second link to Strict Contextual Escaping (SCE) points to a 404.

Closes #8514
2014-09-03 16:31:41 -04:00
thorn0 7af210f1de docs(README): fix 'Contribution guidelines' link
The current link leads to a page 'Building and Testing AngularJS'.
This same link is also included in the 'Building AngularJS' section
of the README where it's more relevant.
2014-09-03 13:25:40 -07:00
TLChan b48203f9f2 docs(tutorial/step_05): improve formatting of code identifier
Closes #8557
2014-09-03 16:25:08 -04:00
Zach Pomerantz 789f0f1809 docs(interpolate): fix link text 2014-09-03 13:20:27 -07:00
Vic Metcalfe 8366d545c5 docs(ngBlur): explain blur events 2014-09-03 13:05:07 -07:00
Caitlin Potter 1f5b4c95eb docs(CHANGELOG.md): add breaking change for a9fcb0d0 (v1.2.13)
Closes #8909
2014-09-03 13:14:02 -04:00
Peter Bacon Darwin 910d652ce5 chore(package.json): update to dgeni-packages v0.9.8
Closes #8860
2014-09-03 11:39:35 +01:00
Nicolai Skogheim e5e62a5479 docs(ngRepeat): update step_02.ngdoc with challenge
Add a simple task for the user to better understand ng-repeat.

Close #8757
2014-09-02 16:46:20 -04:00
Shahar Talmi 8f14b726b0 docs($rootScope): document scope properties 2014-09-02 13:35:41 -07:00
Tiago Ribeiro 88b6a9a288 docs(changelog): fix inline formatting 2014-09-02 13:05:07 -07:00
Matias Niemelä d4fc40c282 docs(changelog): release notes for 1.3.0-RC.0 sonic-boltification 2014-09-02 13:04:45 -07:00
Nima Mehanian 653700df5b docs(guide/providers): fix grammar and punctuation 2014-09-02 12:48:57 -07:00
Shahar Talmi 2712c2f197 fix(ngEventDirs): check scope.$$phase only on $rootScope
Closes #8891, #8849
2014-09-02 10:48:12 -07:00
Shahar Talmi 36e6de1d91 fix(input): check scope.$$phase only on $rootScope 2014-09-02 10:48:12 -07:00
Brian Ford 9bf964f1f3 style(ngRepeatSpec): make jshint happy 2014-08-29 15:51:02 -07:00
Tobias Bosch 54f0bc0fe0 fix(ngEventDirs): execute blur and focus expression using scope.$evalAsync
BREAKING CHANGE:
The `blur` and `focus` event fire synchronously, also during DOM operations
that remove elements. This lead to errors as the Angular model was not
in a consistent state. See this [fiddle](http://jsfiddle.net/fq1dq5yb/) for a demo.

This change executes the expression of those events using
`scope.$evalAsync` if an `$apply` is in progress, otherwise
keeps the old behavior.

Fixes #4979
Fixes #5945
Closes #8803
Closes #6910
Closes #5402
2014-08-29 15:31:27 -07:00
Tobias Bosch 2ece4d0347 fix($browser): detect changes to the browser url that happened in sync
Closes #6976.
2014-08-29 15:31:13 -07:00
Smitha Milli 1812af58c2 fix(ngRepeat): improve errors for duplicate items
-Log the value that had the duplicate key, as well as the key
The error that is thrown when items have duplicate track by keys can be
confusing because only the duplicate key is logged.  If the user didn't
provide that key themselves, they may not know what it is or what item
it corresponds to.
2014-08-29 13:54:57 -07:00
Michael Barton 19cb2e3d12 docs($rootScope): remove duplicate $digest()
Closes #8840
2014-08-29 14:38:31 -04:00
Guilbert c92ce4511b docs(filterFilter): add note on negation 2014-08-28 14:57:30 -07:00
Matias Niemelä d7548fdf1c fix(form): ensure concurrent animations use setClass
When addClass and removeClass are called in parallel it may
cause follow-up animations to get blocked. This fix ensures
that the validity state CSS classes are applied at the same
time via $animate.setClass.

Closes #8166
2014-08-27 23:29:44 -04:00
dennishall1 7e239f9485 docs(dateFilter): add example of string literals in format string
Also changes the wording to include the word "escaped" and "escape", which may help users find the
information they're looking for via searching. (ノ◕ヮ◕)ノ*:・゚✧

Closes #8770
2014-08-25 23:18:53 -04:00
Smith fa5daa7693 docs(guide/di): correct spelling behinds > behind
Closes #8749
2014-08-23 16:38:27 -04:00
69 changed files with 2826 additions and 789 deletions
-4
View File
@@ -1,4 +0,0 @@
{
"directory": "bower_components",
"json": "bower.json"
}
+1 -2
View File
@@ -9,8 +9,7 @@ performance/temp*.html
*.swp
angular.js.tmproj
/node_modules/
/components/
/bower_components/
bower_components/
angular.xcodeproj
.idea
.agignore
+553
View File
@@ -1,3 +1,549 @@
# NOTICE: Pending Breaking Change
The next 1.3.0 release candidate (1.3.0-rc.2) will contain a perf-related change that is likely to
introduce breakages in some applications. The change will affect filters and function call
expressions, and will not call the function if the variables passed to the function are primitive
values and have not changed since the last digest loop.
Example:
```html
//date filter would only be called if the 'timeCreated' property has changed
<span ng-bind="timeCreated|date"></span>
//custom filter would break if depends on data changed by user other than 'cost'
<span ng-bind="cost|i18nLocalizer">
```
<a name="1.3.0-rc.1"></a>
# 1.3.0-rc.1 backyard-atomicity (2014-09-09)
## Bug Fixes
- **$location:**
- don't call toString on null values
([c3a58a9f](https://github.com/angular/angular.js/commit/c3a58a9f34919f121587540e03ecbd51b25198d4))
- remove an unused parameter of $location.url
([99d95f16](https://github.com/angular/angular.js/commit/99d95f1639b64c39231448d77209676b54e6f0be))
- allow numeric location setter arguments
([adb5c6d6](https://github.com/angular/angular.js/commit/adb5c6d6cc76b928436743707727ab0974d6810b),
[#7054](https://github.com/angular/angular.js/issues/7054))
- set `baseHref` in mock browser to `/`
([fc706d13](https://github.com/angular/angular.js/commit/fc706d13d80bb40eb3dade58ea4b92dca33ce4e7),
[#8866](https://github.com/angular/angular.js/issues/8866), [#8889](https://github.com/angular/angular.js/issues/8889))
- **$parse:** disallow passing Function to Array.sort
([bd8ad0fb](https://github.com/angular/angular.js/commit/bd8ad0fbe81f6c280baa26a596d78e58fc7842e6))
- **input:** check `scope.$$phase` only on `$rootScope`
([bf59d727](https://github.com/angular/angular.js/commit/bf59d7274f4a667c5b19e6d4ba5ed2730ca2fe42))
- **ngAnimate:** support removing classes from SVG elements when using jQuery
([b3b67213](https://github.com/angular/angular.js/commit/b3b672130d4d1c6f13bdf7e58be76b2aafea2497),
[#8872](https://github.com/angular/angular.js/issues/8872), [#8893](https://github.com/angular/angular.js/issues/8893))
- **ngEventDirs:** check `scope.$$phase` only on `$rootScope`
([203ea10f](https://github.com/angular/angular.js/commit/203ea10f9ea49d7e29569a4232d3b2a666307cd8),
[#8891](https://github.com/angular/angular.js/issues/8891))
- **ngForm:** don't clear validity of whole form when removing control
([953ee22f](https://github.com/angular/angular.js/commit/953ee22f76f8c1137949ed07f36fafc5bbfeb7fe),
[#8863](https://github.com/angular/angular.js/issues/8863))
- **ngInclude:** correctly add svg-namespaced template content
([6639ca9d](https://github.com/angular/angular.js/commit/6639ca9d6bc00a6e3a31e54c50474361ae3561c6),
[#7538](https://github.com/angular/angular.js/issues/7538), [#8981](https://github.com/angular/angular.js/issues/8981), [#8997](https://github.com/angular/angular.js/issues/8997))
- **ngModel:**
- update model value with async validators correctly
([64c3b745](https://github.com/angular/angular.js/commit/64c3b745fba0792166f30e057f9251f263d80dac))
- render immediately also with async validators
([f94d5515](https://github.com/angular/angular.js/commit/f94d551529b7c970c38b29e3073cec4e7f6b0e00))
- properly parse min/max date values as strings for date inputs
([088545c1](https://github.com/angular/angular.js/commit/088545c1856ce1c3ec3416965dff65077a6e0523),
[#6755](https://github.com/angular/angular.js/issues/6755))
- revalidate the model when min/max expression values change for date inputs
([b3502835](https://github.com/angular/angular.js/commit/b3502835039178296b730b7526e5666b66ba9156),
[#6755](https://github.com/angular/angular.js/issues/6755))
- consider ngMin/ngMax values when validating number input types
([25541c1f](https://github.com/angular/angular.js/commit/25541c1f876a16c892d71faae11727bec7bba98c))
- revalidate the model when min/max expression values change for number inputs
([7b273a2c](https://github.com/angular/angular.js/commit/7b273a2c978d5f5ef374f5335afab0ca7d8cfd4d),
[#2404](https://github.com/angular/angular.js/issues/2404))
- **ngModelOptions:** do not trigger digest on `setViewValue` if debouncing
([e322cd9b](https://github.com/angular/angular.js/commit/e322cd9b3b8b47b95c9de3edf631bb46f919c492),
[#8814](https://github.com/angular/angular.js/issues/8814), [#8850](https://github.com/angular/angular.js/issues/8850), [#8911](https://github.com/angular/angular.js/issues/8911))
- **ngRepeat:** preserve original position of elements that are being animated away
([ed637330](https://github.com/angular/angular.js/commit/ed6373300028deda9a0878b3975699d183c1f75c),
[#8918](https://github.com/angular/angular.js/issues/8918), [#8994](https://github.com/angular/angular.js/issues/8994))
- **ngSwitch:** ensure correct iterator is passed to async function
([712299c2](https://github.com/angular/angular.js/commit/712299c2a24390e74cd5c20f51cb1d78f0233b6f),
[#8833](https://github.com/angular/angular.js/issues/8833))
- **numberFilter:** format numbers that round to zero as nonnegative
([ae952fbf](https://github.com/angular/angular.js/commit/ae952fbf0be925a48743d1c925ffe4e31a42c280),
[#8489](https://github.com/angular/angular.js/issues/8489))
- **orderBy:** allow arrayLike objects to be ordered
([cbdaabfb](https://github.com/angular/angular.js/commit/cbdaabfb59bf3348588d5b581f2754e0f9f034a4),
[#8944](https://github.com/angular/angular.js/issues/8944))
## Features
- **angular.forEach:** add the array/object as the 3rd param like the native array forEach
([df9e60c8](https://github.com/angular/angular.js/commit/df9e60c8e7453cdca2cb5a4fa48f3981ecc23a7d),
[#7902](https://github.com/angular/angular.js/issues/7902))
- **ngModelOptions:** add allowInvalid option
([3c538c1d](https://github.com/angular/angular.js/commit/3c538c1d21c43422c7b4cd9b69cb67981bce2b87),
[#8290](https://github.com/angular/angular.js/issues/8290), [#8313](https://github.com/angular/angular.js/issues/8313))
## Performance Improvements
- **$parse:**
- remove getterFn wrapper for internal use
([b3b476db](https://github.com/angular/angular.js/commit/b3b476db7d34bc2f8b099ab5b993b1e899b9cffd),
[#8901](https://github.com/angular/angular.js/issues/8901))
- removing references to Parser/Lexer from parsed expressions
([43c67ccd](https://github.com/angular/angular.js/commit/43c67ccd167aecc3549e1b7f7d100956204e3ed4))
- calculate array lengths once at start of loop
([907b8c16](https://github.com/angular/angular.js/commit/907b8c1675865ac38dd055f3f304272e68b233d0))
- **extend:** remove use of forEach to remove calls/closures/passing arguments
([9bedeb33](https://github.com/angular/angular.js/commit/9bedeb3353969fba631ad9164edea3c38059fbda),
[#8898](https://github.com/angular/angular.js/issues/8898))
- **jQuery:** only trigger $destroy if a handler exists
([f6aa1c55](https://github.com/angular/angular.js/commit/f6aa1c55616b34215f562e0445e436210860ef04),
[#8859](https://github.com/angular/angular.js/issues/8859))
## Breaking Changes
- **ngModelController,formController:** due to [6046e14b](https://github.com/angular/angular.js/commit/6046e14bd22491168116e61ffdf5fd3fed5f135c),
- `ctrl.$error` no longer contains entries for validators that were
successful.
- `ctrl.$setValidity` now differentiates between `true`, `false`,
`undefined` and `null`, instead of previously only truthy vs falsy.
Closes #8941- **ngSwitch:** due to [0f806d96](https://github.com/angular/angular.js/commit/0f806d9659b5b89a4bd9493364bc36398677e939),
Ever since 0df93fd, tagged in v1.0.0rc1, the ngSwitch directive has had an undocumented `change`
attribute, used for evaluating a scope expression when the switch value changes.
While it's unlikely, applications which may be using this feature should work around the removal
by adding a custom directive which will perform the eval instead. Directive controllers are
re-instantiated when being transcluded, so by putting the attribute on each item that you want
to be notified of a change to, you can more or less emulate the old behaviour.
Example:
```js
angular.module("switchChangeWorkaround", []).
directive("onSwitchChanged", function() {
return {
linke: function($scope, $attrs) {
$scope.$parent.$eval($attrs.change);
}
};
});
```
```html
<div ng-switch="switcher">
<div ng-switch-when="a" on-switch-changed="doSomethingInParentScope()"></div>
<div ng-switch-when="b" on-switch-changed="doSomethingInParentScope()"></div>
</div>
```
Closes #8858
Closes #8822
<a name="1.2.24"></a>
# 1.2.24 static-levitation (2014-09-09)
## Bug Fixes
- **$browser:** detect changes to the browser url that happened in sync
([2ece4d03](https://github.com/angular/angular.js/commit/2ece4d0347a8a18d4d35993bb882ed6b5b24266c),
[#6976](https://github.com/angular/angular.js/issues/6976))
- **$compile:**
- render nested transclusion at the root of a template
([9d9cdfb5](https://github.com/angular/angular.js/commit/9d9cdfb575b89e96ae957c986734a49995e2b511),
[#8914](https://github.com/angular/angular.js/issues/8914), [#8925](https://github.com/angular/angular.js/issues/8925))
- render nested transclusion at the root of a template
([466320f6](https://github.com/angular/angular.js/commit/466320f6911698048bae5406e341d25af7efafa0),
[#8914](https://github.com/angular/angular.js/issues/8914), [#8925](https://github.com/angular/angular.js/issues/8925))
- **$location:**
- don't call toString on null values
([c12e8d46](https://github.com/angular/angular.js/commit/c12e8d4665b635ba6b09d12802efb88d38b7ad5c))
- remove an unused parameter of $location.url
([c65796d4](https://github.com/angular/angular.js/commit/c65796d496038554861e70da8012f9d0e2521e6d))
- allow numeric location setter arguments
([68a09ba7](https://github.com/angular/angular.js/commit/68a09ba74d10a1490feca1d248f85b0023aa399b),
[#7054](https://github.com/angular/angular.js/issues/7054))
- **$parse:** disallow passing Function to Array.sort
([b39e1d47](https://github.com/angular/angular.js/commit/b39e1d47b9a1b39a9fe34c847a81f589fba522f8))
- **form:** ensure concurrent animations use setClass
([d7548fdf](https://github.com/angular/angular.js/commit/d7548fdf1ce6f543bf55d330985a83ef09d0cb83),
[#8166](https://github.com/angular/angular.js/issues/8166))
- **input:** check `scope.$$phase` only on `$rootScope`
([36e6de1d](https://github.com/angular/angular.js/commit/36e6de1d91937d73e900ac115ae366fbefcdf6da))
- **ngEventDirs:**
- check `scope.$$phase` only on `$rootScope`
([2712c2f1](https://github.com/angular/angular.js/commit/2712c2f1979db23eeb53be8a519b9f79bd75e217),
[#8891](https://github.com/angular/angular.js/issues/8891))
- execute `blur` and `focus` expression using `scope.$evalAsync`
([54f0bc0f](https://github.com/angular/angular.js/commit/54f0bc0fe0c6b6d974d23f2c5ef07359dd93eb99),
[#4979](https://github.com/angular/angular.js/issues/4979), [#5945](https://github.com/angular/angular.js/issues/5945), [#8803](https://github.com/angular/angular.js/issues/8803), [#6910](https://github.com/angular/angular.js/issues/6910), [#5402](https://github.com/angular/angular.js/issues/5402))
- **ngRepeat:** improve errors for duplicate items
([1812af58](https://github.com/angular/angular.js/commit/1812af58c2d470d586c2a543c9a7db3f0baca04f))
- **numberFilter:** format numbers that round to zero as nonnegative
([7e02fa07](https://github.com/angular/angular.js/commit/7e02fa07eb5b02e75b1db0058d638af3d1074942),
[#8489](https://github.com/angular/angular.js/issues/8489))
- **orderBy:** allow arrayLike objects to be ordered
([94b0f2d3](https://github.com/angular/angular.js/commit/94b0f2d35de601ded3d93ea4fa78a4d9b139c0a0),
[#8944](https://github.com/angular/angular.js/issues/8944))
## Breaking Changes
- **ngEventDirs:** due to [54f0bc0f](https://github.com/angular/angular.js/commit/54f0bc0fe0c6b6d974d23f2c5ef07359dd93eb99),
The `blur` and `focus` event fire synchronously, also during DOM operations
that remove elements. This lead to errors as the Angular model was not
in a consistent state. See this [fiddle](http://jsfiddle.net/fq1dq5yb/) for a demo.
This change executes the expression of those events using
`scope.$evalAsync` if an `$apply` is in progress, otherwise
keeps the old behavior.
Fixes #4979
Fixes #5945
Closes #8803
Closes #6910
Closes #5402
<a name="1.3.0-RC.0"></a>
# 1.3.0-RC.0 sonic-boltification (2014-08-29)
## Bug Fixes
- **$animate:**
- wait two until two digests are over until enabling animations
([92576743](https://github.com/angular/angular.js/commit/92576743eec0cef5ffdd701b83f72a61e6489c3b),
[#8844](https://github.com/angular/angular.js/issues/8844))
- ensure guarded animations consider AJAX requests upon bootstrap
([4bca4c44](https://github.com/angular/angular.js/commit/4bca4c44b95a7435722605a750804043f2960160),
[#8275](https://github.com/angular/angular.js/issues/8275), [#5262](https://github.com/angular/angular.js/issues/5262))
- use $timeout to handle the delay within staggering animations
([23da6140](https://github.com/angular/angular.js/commit/23da614043fe5dcf0be132b86466eecb11c766a2),
[#7228](https://github.com/angular/angular.js/issues/7228), [#7547](https://github.com/angular/angular.js/issues/7547), [#8297](https://github.com/angular/angular.js/issues/8297), [#8547](https://github.com/angular/angular.js/issues/8547))
- **$browser:** detect changes to the browser url that happened in sync
([3be00df4](https://github.com/angular/angular.js/commit/3be00df495f6eed3b3d9587ebab1fdd633e94e08),
[#6976](https://github.com/angular/angular.js/issues/6976))
- **$compile:** use the correct namespace for transcluded svg elements
([cb73a37c](https://github.com/angular/angular.js/commit/cb73a37c7cae5cdebadf7b3ddd44c5a452495e4e),
[#8808](https://github.com/angular/angular.js/issues/8808), [#8816](https://github.com/angular/angular.js/issues/8816))
- **$location:** always resolve relative links in html5mode to `<base>` url
([22948807](https://github.com/angular/angular.js/commit/22948807e324eb0b182b15b31045dc306a9f3231),
[#8492](https://github.com/angular/angular.js/issues/8492), [#8172](https://github.com/angular/angular.js/issues/8172))
- **$parse:** properly handle dots at the end of identifiers
([8ac90357](https://github.com/angular/angular.js/commit/8ac90357a66ae0c62dbfe6db2c6eaf1d600ecc65),
[#4613](https://github.com/angular/angular.js/issues/4613), [#4912](https://github.com/angular/angular.js/issues/4912), [#8559](https://github.com/angular/angular.js/issues/8559))
- **Angular:** remove duplicate nodeName_ references
([a4520a74](https://github.com/angular/angular.js/commit/a4520a745d917c77f1d12cdbce48272c643f7255))
- **currencyFilter:** pass through null and undefined values
([c2aaddbe](https://github.com/angular/angular.js/commit/c2aaddbe4b21348aab8c13a78cdd6aaee846ae4e),
[#8605](https://github.com/angular/angular.js/issues/8605))
- **docs:** don't throw exception on the 404 page
([550ba01b](https://github.com/angular/angular.js/commit/550ba01b325fc29460030fc9c24fa00269dec2a9),
[#8518](https://github.com/angular/angular.js/issues/8518))
- **input:**
- validate minlength/maxlength for non-string values
([77ce5b89](https://github.com/angular/angular.js/commit/77ce5b89f97aa83c3eb1fe2e19375ef00a822015),
[#7967](https://github.com/angular/angular.js/issues/7967), [#8811](https://github.com/angular/angular.js/issues/8811))
- allow to use seconds in `input[time]` and `input[datetime-local]`
([5f90340a](https://github.com/angular/angular.js/commit/5f90340abb78aa08dde4876328bcc00e46232e46))
- use year 1970 instead of 1900 for `input[time]`
([29f0b568](https://github.com/angular/angular.js/commit/29f0b568debab7810752969d363d337099e96cdc))
- **ngBindHtml:** throw error if interpolation is used in expression
([cd21602d](https://github.com/angular/angular.js/commit/cd21602d5b1650d8be373618cb7320d697e32c4d),
[#8824](https://github.com/angular/angular.js/issues/8824))
- **ngEventDirs:** execute `blur` and `focus` expression using `scope.$evalAsync`
([719c747c](https://github.com/angular/angular.js/commit/719c747cd892ee933e7e414a7dc97e657b88317d),
[#4979](https://github.com/angular/angular.js/issues/4979), [#5945](https://github.com/angular/angular.js/issues/5945), [#8803](https://github.com/angular/angular.js/issues/8803), [#6910](https://github.com/angular/angular.js/issues/6910), [#5402](https://github.com/angular/angular.js/issues/5402))
- **ngModel:**
- always format the viewValue as a string for text, url and email types
([1eda1836](https://github.com/angular/angular.js/commit/1eda18365a348c9597aafba9d195d345e4f13d1e))
- allow non-assignable binding when getterSetter is used
([ab878a6c](https://github.com/angular/angular.js/commit/ab878a6c038f47b95f3a7e85a4fdb599e0c73e63),
[#8704](https://github.com/angular/angular.js/issues/8704))
- treat undefined parse responses as parse errors
([db044c40](https://github.com/angular/angular.js/commit/db044c408a7f8082758b96ab739348810c36e15a))
- **ngRepeat:** improve errors for duplicate items
([0604bb7b](https://github.com/angular/angular.js/commit/0604bb7b7a6156e33679396e805e327662d9a178))
- **ngSwitch:** avoid removing DOM nodes twice within watch operation
([c9b0bfec](https://github.com/angular/angular.js/commit/c9b0bfecc99837af1c97792b3ca3408ba182b0bb),
[#8662](https://github.com/angular/angular.js/issues/8662))
- **numberFilter:** pass through null and undefined values
([2ae10f67](https://github.com/angular/angular.js/commit/2ae10f67fcde3e172f695956301ef796b68a50c2),
[#8605](https://github.com/angular/angular.js/issues/8605), [#8842](https://github.com/angular/angular.js/issues/8842))
## Features
- **core:**
- add angular.reloadWithDebugInfo()
([41c1b88](https://github.com/angular/angular.js/commit/41c1b8858f02c7310bfabdd545ebb28e90eb4258))
- **$animate:**
- use promises instead of callbacks for animations
([bf0f5502](https://github.com/angular/angular.js/commit/bf0f5502b1bbfddc5cdd2f138efd9188b8c652a9))
- coalesce concurrent class-based animations within a digest loop
([2f4437b3](https://github.com/angular/angular.js/commit/2f4437b3a149eafb899f25933bd6c713b167d10e))
- **$compile:**
- bind isolate scope properties to controller
([5f3f25a1](https://github.com/angular/angular.js/commit/5f3f25a1a6f9d4f2a66e2700df3b9c5606f1c255),
[#7635](https://github.com/angular/angular.js/issues/7635), [#7645](https://github.com/angular/angular.js/issues/7645))
- allow disabling scope info
([a1e5cd5f](https://github.com/angular/angular.js/commit/a1e5cd5fe3906ebee8c400247a1f793d3e2239fb))
- **$compile/ngBind:** allow disabling binding info
([3660fd09](https://github.com/angular/angular.js/commit/3660fd0912d3ccf6def8c9f02d8d4c0621c8d91f))
- **$http:** implement mechanism for coalescing calls to $apply in $http
([ea6fc6e6](https://github.com/angular/angular.js/commit/ea6fc6e69c2a2aa213c71ed4e917a0d54d064e4c),
[#8736](https://github.com/angular/angular.js/issues/8736), [#7634](https://github.com/angular/angular.js/issues/7634), [#5297](https://github.com/angular/angular.js/issues/5297))
- **$rootScope:** implement $applyAsync to support combining calls to $apply into a single digest.
([e94d454b](https://github.com/angular/angular.js/commit/e94d454b840f6cc55a440741382b407836ad245b))
- **$templateRequest:** introduce the $templateRequest service
([a70e2833](https://github.com/angular/angular.js/commit/a70e2833ea276107b11aafea96ef4a6724ad4d83))
- **filter:** allow to define the timezone for formatting dates
([4739b1d9](https://github.com/angular/angular.js/commit/4739b1d9daebfd094b6181c5f2cb52ff71e31c61))
- **filterFilter:** pass index to function predicate
([46343c60](https://github.com/angular/angular.js/commit/46343c603db6192daf5303b92eb664749326c7e6),
[#654](https://github.com/angular/angular.js/issues/654))
- **input:** allow to define the timezone for parsing dates
([cc6fc199](https://github.com/angular/angular.js/commit/cc6fc199f5abaacdf781aa03634337d776eb0fc9),
[#8447](https://github.com/angular/angular.js/issues/8447))
- **minErr:** allow specifying ErrorConstructor in minErr constructor
([a6bd4bc8](https://github.com/angular/angular.js/commit/a6bd4bc866a18f860c7548fa1b3f6d4c2a953416))
- **ngModel:** provide validation API functions for sync and async validations
([2ae4f40b](https://github.com/angular/angular.js/commit/2ae4f40be1803d999ca2a8cc30ec17ff19ea6d86))
- **ngRoute:** alias string as redirectTo property in .otherwise()
([3b5d75c0](https://github.com/angular/angular.js/commit/3b5d75c021e21fa6ec4dc6c47b8eafa55680ea63),
[#7794](https://github.com/angular/angular.js/issues/7794))
- **testability:** add $$testability service
([85880a64](https://github.com/angular/angular.js/commit/85880a64900fa22a61feb926bf52de0965332ca5))
## Performance Improvements
- **$compile:**
- add debug classes in compile phase
([e0489abd](https://github.com/angular/angular.js/commit/e0489abd8d9e4971ae23cc38805a92d227d1f3a1))
- only iterate over elements with link functions
([fdf9989f](https://github.com/angular/angular.js/commit/fdf9989f7cf1ed81982a788b75a338ac33334571),
[#8741](https://github.com/angular/angular.js/issues/8741))
- **nodeName_:** simplify the code and reduce the number of DOM calls
([5a1a0c96](https://github.com/angular/angular.js/commit/5a1a0c96220101b5e040f0755e5eb401e2c73f65))
- **select:** execute render after $digest cycle
([6f7018d5](https://github.com/angular/angular.js/commit/6f7018d52fa4f9f9c7fa8e3035317d1239efb20f),
[#8825](https://github.com/angular/angular.js/issues/8825))
## Breaking Changes
- **$location**: due to [22948807](https://github.com/angular/angular.js/commit/22948807e324eb0b182b15b31045dc306a9f3231)
#### since 1.2.0 and 1.3.0-beta.1
Angular now requires a `<base>` tag when html5 mode of `$location` is enabled. Reasoning:
Using html5 mode without a `<base href="...">` tag makes relative links for images, links, ...
relative to the current url if the browser supports
the history API. However, if the browser does not support the history API Angular falls back to using the `#`,
and then all those relative links would be broken.
The `<base>` tag is also needed when a deep url is loaded from the server, e.g. `http://server/some/page/url`.
In that case, Angular needs to decide which part of the url is the base of the application, and which part
is path inside of the application.
To summarize: Now all relative links are always relative to the `<base>` tag.
Exception (also a breaking change):
Link tags whose `href` attribute starts with a `#` will only change the hash of the url, but nothing else
(e.g. `<a href="#someAnchor">`). This is to make it easy to scroll to anchors inside a document.
Related to #6162
Closes #8492
#### since 1.2.17 and 1.3.0-beta.10
In html5 mode without a `<base>` tag on older browser that don't support the history API
relative paths were adding up. E.g. clicking on `<a href="page1">` and then on `<a href="page2">`
would produce `$location.path()==='/page1/page2'`. The code that introduced this behavior was removed
and Angular now also requires a `<base>` tag to be present when using html5 mode.
Closes #8172, #8233
- **ngInclude, ngMessage, ngView and directives that load templates**: due to [a70e2833](https://github.com/angular/angular.js/commit/a70e2833ea276107b11aafea96ef4a6724ad4d83)
Angular will now throw a $compile minErr each a template fails to download
for ngView, directives and ngMessage template requests. This changes the former
behavior of silently ignoring failed HTTP requests--or when the template itself
is empty. Please ensure that all directive, ngView and ngMessage code now properly
addresses this scenario. NgInclude is uneffected from this change.
- **$animate**: due to [23da6140](https://github.com/angular/angular.js/commit/23da614043fe5dcf0be132b86466eecb11c766a2)
If any stagger code consisted of having BOTH transition staggers and delay staggers
together then that will not work the same way. Angular will now instead choose
the highest stagger delay value and set the timeout to wait for that before
applying the active CSS class.
- **$animate**: due to [bf0f5502](https://github.com/angular/angular.js/commit/bf0f5502b1bbfddc5cdd2f138efd9188b8c652a9)
Both the API for the cancallation method and the done callback for
$animate animations is different. Instead of using a callback function
for each of the $animate animation methods, a promise is used instead.
```js
//before
$animate.enter(element, container, null, callbackFn);
//after
$animate.enter(element, container).then(callbackFn);
```
The animation can now be cancelled via `$animate.cancel(promise)`.
```js
//before
var cancelFn = $animate.enter(element, container);
cancelFn(); //cancels the animation
//after
var promise = $animate.enter(element, container);
$animate.cancel(promise); //cancels the animation
```
keep in mind that you will still need to run $scope.$apply inside of the `then` callback
to trigger a digest.
- **$animate**: due to [2f4437b3](https://github.com/angular/angular.js/commit/2f4437b3a149eafb899f25933bd6c713b167d10e)
$animate.addClass, $animate.removeClass and $animate.setClass will no longer start the animation
right after being called in the directive code. The animation will only commence once a digest
has passed. This means that all animation-related testing code requires an extra digest to kick
off the animation.
```js
//before this fix
$animate.addClass(element, 'super');
expect(element).toHaveClass('super');
//now
$animate.addClass(element, 'super');
$rootScope.$digest();
expect(element).toHaveClass('super');
```
$animate will also tally the amount of times classes are added and removed and only animate
the left over classes once the digest kicks in. This means that for any directive code that
adds and removes the same CSS class on the same element then this may result in no animation
being triggered at all.
```js
$animate.addClass(element, 'klass');
$animate.removeClass(element, 'klass');
$rootScope.$digest();
//nothing happens...
```
- **$compile/ngBind:** due to [3660fd09](https://github.com/angular/angular.js/commit/3660fd0912d3ccf6def8c9f02d8d4c0621c8d91f),
The value of `$binding` data property on an element is always an array now
and the expressions do not include the curly braces `{{ ... }}`.
- **currencyFilter:** due to [c2aaddbe](https://github.com/angular/angular.js/commit/c2aaddbe4b21348aab8c13a78cdd6aaee846ae4e),
previously the currency filter would convert null and undefined values into empty string, after this change
these values will be passed through.
Only cases when the currency filter is chained with another filter that doesn't expect null/undefined will be affected. This
should be very rare.
This change will not change the visual output of the filter because the interpolation will convert the null/undefined to
an empty string.
Closes #8605
- **numberFilter:** due to [2ae10f67](https://github.com/angular/angular.js/commit/2ae10f67fcde3e172f695956301ef796b68a50c2),
previously the number filter would convert null and undefined values into empty string, after this change
these values will be passed through.
Only cases when the number filter is chained with another filter that doesn't expect null/undefined will be affected. This
should be very rare.
This change will not change the visual output of the filter because the interpolation will convert the null/undefined to
an empty string.
Closes #8605
Closes #8842
- **input:**
- due to [77ce5b89](https://github.com/angular/angular.js/commit/77ce5b89f97aa83c3eb1fe2e19375ef00a822015),
NgModel.viewValue will always be used when rendering validations for `minlength` and `maxlength`.
Closes #7967
Closes #8811
- **input:**
- due to [29f0b568](https://github.com/angular/angular.js/commit/29f0b568debab7810752969d363d337099e96cdc),
According to the HTML5 spec `input[time]` should create dates
based on the year 1970 (used to be based on the year 1900).
Related to #8447.
- **ngModel**: due to [db044c40](https://github.com/angular/angular.js/commit/db044c408a7f8082758b96ab739348810c36e15a)
Any parser code from before that returned an `undefined` value
(or nothing at all) will now cause a parser failure. When this occurs
none of the validators present in `$validators` will run until the parser
error is gone. The error will be stored on `ngModel.$error`.
- **ngEventDirs:** due to [719c747c](https://github.com/angular/angular.js/commit/719c747cd892ee933e7e414a7dc97e657b88317d),
The `blur` and `focus` event fire synchronously, also during DOM operations
that remove elements. This lead to errors as the Angular model was not
in a consistent state. See this [fiddle](http://jsfiddle.net/fq1dq5yb/) for a demo.
This change executes the expression of those events using
`scope.$evalAsync` if an `$apply` is in progress, otherwise
keeps the old behavior.
Fixes #4979
Fixes #5945
Closes #8803
Closes #6910
Closes #5402
<a name="1.2.23"></a>
# 1.2.23 superficial-malady (2014-08-22)
@@ -1668,6 +2214,13 @@ jQuery. We don't expect that app code actually depends on this accidental featur
application if 100s of elements are being inserted into the page. Therefore after this
change callbacks are only fired if registered on the element being animated.
- **input:**
- due to [a9fcb0d0](https://github.com/angular/angular.js/commit/a9fcb0d0fc6456f80501b8820d02b04d7c15b6d6),
input[type=file] will no longer support ngModel. Due to browser support being spotty among target browsers,
file inputs cannot be cleanly supported, and even features which technically do work (such as ng-change)
work in an inconsistent way depending on the attributes of the form control.
As a workaround, one can manually listen for change events on file inputs and handle them manually.
<a name="1.2.12"></a>
# 1.2.12 cauliflower-eradication (2014-02-07)
+2 -2
View File
@@ -15,7 +15,7 @@ it makes development fun!
* Tutorial: http://docs.angularjs.org/tutorial
* API Docs: http://docs.angularjs.org/api
* Developer Guide: http://docs.angularjs.org/guide
* Contribution guidelines: http://docs.angularjs.org/misc/contribute
* Contribution guidelines: [CONTRIBUTING.md](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md)
* Dashboard: http://dashboard.angularjs.org
Building AngularJS
@@ -37,7 +37,7 @@ To execute end-to-end (e2e) tests, use:
grunt test:e2e
To learn more about the grunt tasks, run `grunt --help` and also read our
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
[contribution guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md).
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/README.md?pixel)](https://github.com/igrigorik/ga-beacon)
+1 -5
View File
@@ -2,11 +2,7 @@
"name": "AngularJS",
"devDependencies": {
"jquery": "1.10.2",
"lunr.js": "0.4.3",
"open-sans-fontface": "1.0.4",
"google-code-prettify": "1.0.1",
"closure-compiler": "https://closure-compiler.googlecode.com/files/compiler-20130603.zip",
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip",
"bootstrap": "3.1.1"
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip"
}
}
+6 -4
View File
@@ -635,12 +635,14 @@ ul.events > li {
display:inline-block;
padding:3px 0;
}
.nav-index-group .nav-index-listing:not(.nav-index-section) + .nav-index-listing:not(.nav-index-section):after {
padding-right:5px;
content:", ";
.nav-index-group .nav-index-listing:not(.nav-index-section):after {
padding-right:5px;
margin-left:-3px;
content:", ";
}
.nav-index-group .nav-index-listing:last-child {
.nav-index-group .nav-index-listing:last-child:after {
content:"";
display:inline-block;
}
.nav-index-group .nav-index-section {
display:block;
+3 -1
View File
@@ -74,6 +74,7 @@ angular.module('search', [])
var index = lunrSearch(function() {
this.ref('id');
this.field('title', {boost: 50});
this.field('members', { boost: 40});
this.field('keywords', { boost : 20 });
});
@@ -82,7 +83,8 @@ angular.module('search', [])
index.store({
id : key,
title : page.searchTerms.titleWords,
keywords : page.searchTerms.keywords
keywords : page.searchTerms.keywords,
members : page.searchTerms.members
});
};
});
+10
View File
@@ -0,0 +1,10 @@
{
"name": "AngularJS-docs-app",
"dependencies": {
"jquery": "2.1.1",
"lunr.js": "0.4.3",
"open-sans-fontface": "1.0.4",
"google-code-prettify": "1.0.1",
"bootstrap": "3.1.1"
}
}
+2 -1
View File
@@ -22,7 +22,8 @@ module.exports = function(config) {
]);
config.append('processing.tagDefinitions', [
require('./tag-defs/tutorial-step')
require('./tag-defs/tutorial-step'),
require('./tag-defs/sortOrder')
]);
config.append('processing.defaultTagTransforms', [
+15 -3
View File
@@ -1,3 +1,4 @@
"use strict";
var _ = require('lodash');
var log = require('winston');
var fs = require('fs');
@@ -5,7 +6,7 @@ var path = require('canonical-path');
module.exports = {
name: 'keywords',
runAfter: ['docs-processed'],
runAfter: ['docs-processed', 'api-docs'],
runBefore: ['adding-extra-docs'],
description: 'This processor extracts all the keywords from the document',
process: function(docs, config) {
@@ -52,7 +53,7 @@ module.exports = {
_.forEach(tokens, function(token){
var match = token.match(KEYWORD_REGEX);
if (match){
key = match[1];
var key = match[1];
if ( !keywordMap[key]) {
keywordMap[key] = true;
words.push(key);
@@ -69,17 +70,28 @@ module.exports = {
var words = [];
var keywordMap = _.clone(ignoreWordsMap);
var members = [];
var membersMap = {};
// Search each top level property of the document for search terms
_.forEach(doc, function(value, key) {
if ( _.isString(value) && !propertiesToIgnore[key] ) {
extractWords(value, words, keywordMap);
}
if ( key === 'methods' || key === 'properties' || key === 'events' ) {
_.forEach(value, function(member) {
extractWords(member.name, members, membersMap);
});
}
});
doc.searchTerms = {
titleWords: extractTitleWords(doc.name),
keywords: _.sortBy(words).join(' ')
keywords: _.sortBy(words).join(' '),
members: _.sortBy(members).join(' ')
};
});
+14 -7
View File
@@ -119,13 +119,20 @@ var navGroupMappers = {
})];
},
pages: function(pages, area) {
return [getNavGroup(pages, area, 'path', function(page) {
return {
name: page.name,
href: page.path,
type: 'page'
};
})];
return [getNavGroup(
pages,
area,
function(page) {
return page.sortOrder || page.path;
},
function(page) {
return {
name: page.name,
href: page.path,
type: 'page'
};
}
)];
}
};
+6
View File
@@ -0,0 +1,6 @@
module.exports = {
name: 'sortOrder',
transforms: function(doc, tag, value) {
return parseInt(value, 10);
}
};
+2 -2
View File
@@ -10,7 +10,7 @@ Angular's {@link ng.$sce Strict Contextual Escaping (SCE)} mode
contexts to result in a value that is trusted as safe for use in such a context. (e.g. loading an
Angular template from a URL requires that the URL is one considered safe for loading resources.)
This helps prevent XSS and other security issues. Read more at {@link
api/ng.$sce Strict Contextual Escaping (SCE)}
This helps prevent XSS and other security issues. Read more at
{@link ng.$sce Strict Contextual Escaping (SCE)}
You may want to include the ngSanitize module to use the automatic sanitizing.
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Using $location
@sortOrder 500
@description
# What does it do?
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Animations
@sortOrder 310
@description
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Bootstrap
@sortOrder 350
@description
# Bootstrap
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name HTML Compiler
@sortOrder 330
@description
<div class="alert alert-warning">
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Conceptual Overview
@sortOrder 200
@description
# Conceptual Overview
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Controllers
@sortOrder 220
@description
# Understanding Controllers
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Working With CSS
@sortOrder 510
@description
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Data Binding
@sortOrder 210
@description
Data-binding in Angular apps is the automatic synchronization of data between the model and view
+2 -1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Dependency Injection
@sortOrder 250
@description
# Dependency Injection
@@ -109,7 +110,7 @@ asks the injector to create an instance of the controller and its dependencies.
injector.instantiate(MyController);
```
This is all done behinds the scenes. Notice that by having the `ng-controller` ask the injector to
This is all done behind the scenes. Notice that by having the `ng-controller` ask the injector to
instantiate the class, it can satisfy all of the dependencies of `MyController` without the
controller ever knowing about the injector.
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Directives
@sortOrder 300
@description
# Creating Custom Directives
+1 -1
View File
@@ -1,6 +1,6 @@
@workInProgress
@ngdoc overview
@name E2E Testing
@sortOrder 420
@description
# E2E Testing
+4 -1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Expressions
@sortOrder 270
@description
# Angular Expressions
@@ -38,7 +39,9 @@ the method from your view. If you want to `eval()` an Angular expression yoursel
## Example
<example>
<file name="index.html">
1+2={{1+2}}
<span>
1+2={{1+2}}
</span>
</file>
<file name="protractor.js" type="protractor">
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Filters
@sortOrder 280
@description
A filter formats the value of an expression for display to the user. They can be used in view templates,
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Forms
@sortOrder 290
@description
Controls (`input`, `select`, `textarea`) are ways for a user to enter data.
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name i18n and l10n
@sortOrder 520
@description
# i18n and l10n
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Internet Explorer Compatibility
@sortOrder 530
@description
# Internet Explorer Compatibility
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Introduction
@sortOrder 100
@description
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Migrating from 1.0 to 1.2
@sortOrder 550
@description
# Migrating from 1.0 to 1.2
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Modules
@sortOrder 320
@description
# What is a Module?
+18 -18
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Providers
@sortOrder 340
@description
# Providers
@@ -97,9 +98,8 @@ created by this recipe.
Note: All services in Angular are singletons. That means that the injector uses each recipe at most
once to create the object. The injector then caches the reference for all future needs.
Since Factory is more a powerful version of the Value recipe, you can construct the same service with it.
Using our previous `clientId` Value recipe example, we can rewrite it as a Factory recipe like
this:
Since a Factory is a more powerful version of the Value recipe, the same service can be constructed with it.
Using our previous `clientId` Value recipe example, we can rewrite it as a Factory recipe like this:
```javascript
myApp.factory('clientId', function clientIdFactory() {
@@ -134,11 +134,11 @@ token.
<div class="alert alert-success">
**Best Practice:** name the factory functions as `<serviceId>Factory`
(e.g. apiTokenFactory). While this naming convention is not required, it helps when navigating the code base
(e.g., apiTokenFactory). While this naming convention is not required, it helps when navigating the codebase
or looking at stack traces in the debugger.
</div>
Just like with Value recipe, Factory recipe can create a service of any type, whether it be a
Just like with the Value recipe, the Factory recipe can create a service of any type, whether it be a
primitive, object literal, function, or even an instance of a custom type.
@@ -153,7 +153,7 @@ function UnicornLauncher(apiToken) {
this.launchedCount = 0;
this.launch = function() {
// make a request to the remote api and include the apiToken
// Make a request to the remote API and include the apiToken
...
this.launchedCount++;
}
@@ -170,7 +170,7 @@ myApp.factory('unicornLauncher', ["apiToken", function(apiToken) {
```
This is, however, exactly the use-case that Service recipe is the most suitable for.
This is, however, exactly the use-case that the Service recipe is the most suitable for.
The Service recipe produces a service just like the Value or Factory recipes, but it does so by
*invoking a constructor with the `new` operator*. The constructor can take zero or more arguments,
@@ -189,7 +189,7 @@ myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);
Much simpler!
Note: Yes, we have called one of our service recipes 'Service'. We regret this and know that we'll
be somehow punished for our mis-deed. It's like we named one of our offspring 'Child'. Boy,
be somehow punished for our misdeed. It's like we named one of our offspring 'Child'. Boy,
that would mess with the teachers.
@@ -199,8 +199,8 @@ As already mentioned in the intro, the Provider recipe is the core recipe type a
all the other recipe types are just syntactic sugar on top of it. It is the most verbose recipe
with the most abilities, but for most services it's overkill.
Provider recipe is syntactically defined as a custom type that implements a `$get` method. This
method is a factory function just like the one we use in Factory recipe. In fact, if you define
The Provider recipe is syntactically defined as a custom type that implements a `$get` method. This
method is a factory function just like the one we use in the Factory recipe. In fact, if you define
a Factory recipe, an empty Provider type with the `$get` method set to your factory function is
automatically created under the hood.
@@ -248,7 +248,7 @@ and wires (injects) all provider instances only.
During application bootstrap, before Angular goes off creating all services, it configures and
instantiates all providers. We call this the configuration phase of the application life-cycle.
During this phase services aren't accessible because they haven't been created yet.
During this phase, services aren't accessible because they haven't been created yet.
Once the configuration phase is over, interaction with providers is disallowed and the process of
creating services starts. We call this part of the application life-cycle the run phase.
@@ -259,9 +259,9 @@ creating services starts. We call this part of the application life-cycle the ru
We've just learned how Angular splits the life-cycle into configuration phase and run phase and how
you can provide configuration to your application via the config function. Since the config
function runs in the configuration phase when no services are available, it doesn't have access
even to simple value objects created via Value recipe.
even to simple value objects created via the Value recipe.
Since simple values, like url prefix, don't have dependencies or configuration, it is often handy
Since simple values, like URL prefixes, don't have dependencies or configuration, it's often handy
to make them available in both the configuration and run phases. This is what the Constant recipe
is for.
@@ -317,7 +317,7 @@ Let's take a look at how we would create a very simple component via the directi
on the `planetName` constant we've just defined and displays the planet name, in our case:
"Planet Name: Greasy Giant".
Since the directives are registered via Factory recipe, we can use the same syntax as with factories.
Since the directives are registered via the Factory recipe, we can use the same syntax as with factories.
```javascript
myApp.directive('myPlanet', ['planetName', function myPlanetDirectiveFactory(planetName) {
@@ -340,7 +340,7 @@ We can then use the component like this:
</html>
```
Using Factory recipes you can also define Angular's filters and animations, but the controllers
Using Factory recipes, you can also define Angular's filters and animations, but the controllers
are a bit special. You create a controller as a custom type that declares its dependencies as
arguments for its constructor function. This constructor is then registered with a module. Let's
take a look at the `DemoController`, created in one of the early examples:
@@ -351,7 +351,7 @@ myApp.controller('DemoController', ['clientId', function DemoController(clientId
}]);
```
The DemoController is instantiated via its constructor every time the app needs an instance of
The DemoController is instantiated via its constructor, every time the app needs an instance of
DemoController (in our simple app it's just once). So unlike services, controllers are not
singletons. The constructor is called with all the requested services, in our case the `clientId`
service.
@@ -365,12 +365,12 @@ To wrap it up, let's summarize the most important points:
- There are five recipe types that define how to create objects: Value, Factory, Service, Provider
and Constant.
- Factory and Service are the most commonly used recipes. The only difference between them is that
Service recipe works better for objects of custom type, while Factory can produce JavaScript
the Service recipe works better for objects of a custom type, while the Factory can produce JavaScript
primitives and functions.
- The Provider recipe is the core recipe type and all the other ones are just syntactic sugar on it.
- Provider is the most complex recipe type. You don't need it unless you are building a reusable
piece of code that needs global configuration.
- All special purpose objects except for Controller are defined via Factory recipes.
- All special purpose objects except for the Controller are defined via Factory recipes.
<table class="table table-bordered code-table">
<thead>
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Scopes
@sortOrder 240
@description
# What are Scopes?
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Services
@sortOrder 230
@description
# Services
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Templates
@sortOrder 260
@description
In Angular, templates are written with HTML that contains Angular-specific elements and attributes.
+1
View File
@@ -1,5 +1,6 @@
@ngdoc overview
@name Unit Testing
@sortOrder 410
@description
JavaScript is a dynamically typed language which comes with great power of expression, but it also
+2
View File
@@ -250,6 +250,8 @@ browser is limited, which results in your karma tests running extremely slow.
<tr><th>row number</th></tr>
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
</table>
Extra points: try and make an 8x8 table using an additional ng-repeat.
* Make the unit test fail by changing `expect(scope.phones.length).toBe(3)` to instead use `toBe(4)`.
+1 -1
View File
@@ -166,7 +166,7 @@ Because we started using dependency injection and our controller has dependencie
controller in our tests is a bit more complicated. We could use the `new` operator and provide the
constructor with some kind of fake `$http` implementation. However, Angular provides a mock `$http`
service that we can use in unit tests. We configure "fake" responses to server requests by calling
methods on a service called $httpBackend:
methods on a service called `$httpBackend`:
```js
describe('PhoneCat controllers', function() {
+5 -2
View File
@@ -1,3 +1,5 @@
"use strict";
var path = require('canonical-path');
var versionInfo = require('../lib/versions/version-info');
var basePath = __dirname;
@@ -9,9 +11,10 @@ module.exports = function(config) {
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + versionInfo.cdnVersion;
var getVersion = function(component, sourceFolder, packageFile) {
sourceFolder = sourceFolder || '../bower_components';
sourceFolder = sourceFolder || './bower_components';
packageFile = packageFile || 'bower.json';
return require(path.join(sourceFolder,component,packageFile)).version;
return require(path.resolve(sourceFolder,component,packageFile)).version;
};
+13 -3
View File
@@ -1,4 +1,7 @@
"use strict";
var gulp = require('gulp');
var log = require('gulp-util').log;
var concat = require('gulp-concat');
var jshint = require('gulp-jshint');
var bower = require('bower');
@@ -12,7 +15,7 @@ var path = require('canonical-path');
// See clean and bower for async tasks, and see assets and doc-gen for dependent tasks below
var outputFolder = '../build/docs';
var bowerFolder = '../bower_components';
var bowerFolder = 'bower_components';
var copyComponent = function(component, pattern, sourceFolder, packageFile) {
@@ -26,7 +29,14 @@ var copyComponent = function(component, pattern, sourceFolder, packageFile) {
};
gulp.task('bower', function() {
return bower.commands.install();
var bowerTask = bower.commands.install();
bowerTask.on('log', function (result) {
log('bower:', result.id, result.data.endpoint.name);
});
bowerTask.on('error', function(error) {
log(error);
});
return bowerTask;
});
gulp.task('build-app', function() {
@@ -48,7 +58,7 @@ gulp.task('assets', ['bower'], function() {
});
gulp.task('doc-gen', function() {
gulp.task('doc-gen', ['bower'], function() {
var generateDocs = dgeni.generator('docs.config.js');
return generateDocs()
.catch(function(error) {
+1770 -604
View File
File diff suppressed because it is too large Load Diff
+31 -25
View File
@@ -6,11 +6,20 @@
"url": "https://github.com/angular/angular.js.git"
},
"devDependencies": {
"angular-benchpress": "0.x.x",
"benchmark": "1.x.x",
"bower": "~1.3.9",
"browserstacktunnel-wrapper": "~1.1.1",
"canonical-path": "0.0.2",
"dgeni": "^0.3.0",
"dgeni-packages": "^0.9.8",
"es6-shim": "^0.14.0",
"event-stream": "~3.1.0",
"grunt": "~0.4.2",
"grunt-bump": "~0.0.13",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-connect": "~0.5.0",
"grunt-contrib-compress": "~0.5.2",
"grunt-contrib-connect": "~0.5.0",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-jshint": "~0.10.0",
"grunt-ddescribe-iit": "~0.0.1",
@@ -19,41 +28,38 @@
"grunt-merge-conflict": "~0.0.1",
"grunt-parallel": "~0.3.1",
"grunt-shell": "~0.4.0",
"load-grunt-tasks": "~0.3.0",
"bower": "~1.2.2",
"gulp": "~3.8.0",
"gulp-concat": "~2.1.7",
"gulp-jshint": "~1.4.2",
"gulp-util": "^3.0.1",
"jasmine-node": "~1.11.0",
"q": "~1.0.0",
"shelljs": "~0.2.6",
"jasmine-reporters": "~0.2.1",
"jshint-stylish": "~0.1.5",
"karma": "^0.12.0",
"karma-jasmine": "0.1.5",
"karma-browserstack-launcher": "0.0.7",
"karma-chrome-launcher": "0.1.2",
"karma-firefox-launcher": "0.1.3",
"karma-ng-scenario": "0.1.0",
"karma-jasmine": "0.1.5",
"karma-junit-reporter": "0.2.1",
"karma-ng-scenario": "0.1.0",
"karma-sauce-launcher": "0.2.0",
"karma-script-launcher": "0.1.0",
"karma-browserstack-launcher": "0.0.7",
"protractor": "1.0.0",
"yaml-js": "~0.0.8",
"rewire": "1.1.3",
"promises-aplus-tests": "~1.3.2",
"semver": "~2.1.0",
"load-grunt-tasks": "~0.3.0",
"lodash": "~2.1.0",
"browserstacktunnel-wrapper": "~1.1.1",
"jasmine-reporters": "~0.2.1",
"gulp": "~3.4.0",
"event-stream": "~3.1.0",
"marked": "~0.3.0",
"gulp-concat": "~2.1.7",
"canonical-path": "0.0.2",
"winston": "~0.7.2",
"dgeni": "^0.3.0",
"dgeni-packages": "^0.9.7",
"gulp-jshint": "~1.4.2",
"jshint-stylish": "~0.1.5",
"node-html-encoder": "0.0.2",
"promises-aplus-tests": "~1.3.2",
"protractor": "1.2.0",
"q": "~1.0.0",
"q-io": "^1.10.9",
"qq": "^0.3.5",
"rewire": "1.1.3",
"semver": "~2.1.0",
"shelljs": "~0.2.6",
"sorted-object": "^1.0.0",
"qq": "^0.3.5"
"stringmap": "^0.2.2",
"winston": "~0.7.2",
"yaml-js": "~0.0.8"
},
"licenses": [
{
-3
View File
@@ -23,9 +23,6 @@ function cleanModule(module, name) {
if (name === 'chokidar') {
if (module.version === '0.8.1') {
delete module.dependencies;
} else if ( module.version !== '0.8.2') {
throw new Error("Unfamiliar chokidar version (v" + module.version +
") , please check status of https://github.com/paulmillr/chokidar/pull/106");
}
}
+7
View File
@@ -234,6 +234,13 @@ function Browser(window, document, $log, $sniffer) {
return callback;
};
/**
* Checks whether the url has changed outside of Angular.
* Needs to be exported to be able to check for changes that have been done in sync,
* as hashchange/popstate events fire in async.
*/
self.$$checkUrlChange = fireUrlChange;
//////////////////////////////////////////////////////////////
// Misc API
//////////////////////////////////////////////////////////////
+5 -4
View File
@@ -70,8 +70,9 @@ function FormController(element, attrs, $scope, $animate) {
// convenience method for easy toggling of classes
function toggleValidCss(isValid, validationErrorKey) {
validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
$animate.removeClass(element, (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey);
$animate.addClass(element, (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
$animate.setClass(element,
(isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey,
(isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey);
}
/**
@@ -286,8 +287,6 @@ function FormController(element, attrs, $scope, $animate) {
* hitting enter in any of the input fields will trigger the click handler on the *first* button or
* input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
*
* @param {string=} name Name of the form. If specified, the form controller will be published into
* related scope, under this name.
*
* ## Animation Hooks
*
@@ -365,6 +364,8 @@ function FormController(element, attrs, $scope, $animate) {
</file>
</example>
*
* @param {string=} name Name of the form. If specified, the form controller will be published into
* related scope, under this name.
*/
var formDirectiveFactory = function(isNgForm) {
return ['$timeout', function($timeout) {
+1 -1
View File
@@ -522,7 +522,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
// a row.
var revalidate = validity && ctrl.$$hasNativeValidators;
if (ctrl.$viewValue !== value || (value === '' && revalidate)) {
if (scope.$$phase) {
if (scope.$root.$$phase) {
ctrl.$setViewValue(value);
} else {
scope.$apply(function() {
+33 -5
View File
@@ -19,7 +19,9 @@
<button ng-click="count = count + 1" ng-init="count=0">
Increment
</button>
count: {{count}}
<span>
count: {{count}}
<span>
</file>
<file name="protractor.js" type="protractor">
it('should check ng-click', function() {
@@ -37,19 +39,33 @@
* Events that are handled via these handler are always configured not to propagate further.
*/
var ngEventDirectives = {};
// For events that might fire synchronously during DOM manipulation
// we need to execute their event handlers asynchronously using $evalAsync,
// so that they are not executed in an inconsistent state.
var forceAsyncEvents = {
'blur': true,
'focus': true
};
forEach(
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
function(name) {
var directiveName = directiveNormalize('ng-' + name);
ngEventDirectives[directiveName] = ['$parse', function($parse) {
ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
return {
compile: function($element, attr) {
var fn = $parse(attr[directiveName]);
return function ngEventHandler(scope, element) {
element.on(lowercase(name), function(event) {
scope.$apply(function() {
var eventName = lowercase(name);
element.on(eventName, function(event) {
var callback = function() {
fn(scope, {$event:event});
});
};
if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
scope.$evalAsync(callback);
} else {
scope.$apply(callback);
}
});
};
}
@@ -366,6 +382,10 @@ forEach(
* @description
* Specify custom behavior on focus event.
*
* Note: As the `focus` event is executed synchronously when calling `input.focus()`
* AngularJS executes the expression using `scope.$evalAsync` if the event is fired
* during an `$apply` to ensure a consistent state.
*
* @element window, input, select, textarea, a
* @priority 0
* @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
@@ -382,6 +402,14 @@ forEach(
* @description
* Specify custom behavior on blur event.
*
* A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
* an element has lost focus.
*
* Note: As the `blur` event is executed synchronously also during DOM manipulations
* (e.g. removing a focussed input),
* AngularJS executes the expression using `scope.$evalAsync` if the event is fired
* during an `$apply` to ensure a consistent state.
*
* @element window, input, select, textarea, a
* @priority 0
* @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
+3 -2
View File
@@ -309,8 +309,9 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
if (block && block.scope) lastBlockMap[block.id] = block;
});
// This is a duplicate and we need to throw an error
throw ngRepeatMinErr('dupes', "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}",
expression, trackById);
throw ngRepeatMinErr('dupes',
"Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
expression, trackById, toJson(value));
} else {
// new never before seen block
nextBlockOrder[index] = { id: trackById };
+1 -11
View File
@@ -60,16 +60,6 @@
* For more information about how angular filters work, and how to create your own filters, see
* {@link guide/filter Filters} in the Angular Developer Guide.
*/
/**
* @ngdoc method
* @name $filterProvider#register
* @description
* Register filter factory function.
*
* @param {String} name Name of the filter.
* @param {Function} fn The filter factory function which is injectable.
*/
/**
* @ngdoc service
@@ -108,7 +98,7 @@ function $FilterProvider($provide) {
/**
* @ngdoc method
* @name $controllerProvider#register
* @name $filterProvider#register
* @param {string|Object} name Name of the filter function, or an object map of filters where
* the keys are the filter names and the values are the filter factories.
* @returns {Object} Registered filter instance, or if a map of filters was provided then a map
+3 -1
View File
@@ -23,7 +23,9 @@
* which have property `name` containing "M" and property `phone` containing "1". A special
* property name `$` can be used (as in `{$:"text"}`) to accept a match against any
* property of the object. That's equivalent to the simple substring match with a `string`
* as described above.
* as described above. The predicate can be negated by prefixing the string with `!`.
* For Example `{name: "!M"}` predicate will return an array of items which have property `name`
* not containing "M".
*
* - `function(value)`: A predicate function can be used to write arbitrary filters. The function is
* called for each element of `array`. The final result is an array of those elements that
+10 -2
View File
@@ -153,6 +153,10 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
if (number === 0) {
isNegative = false;
}
var fraction = ('' + number).split(DECIMAL_SEP);
var whole = fraction[0];
fraction = fraction[1] || '';
@@ -322,8 +326,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
* * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm)
* * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm)
*
* `format` string can contain literal values. These need to be quoted with single quotes (e.g.
* `"h 'in the morning'"`). In order to output single quote, use two single quotes in a sequence
* `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
* `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
* (e.g. `"h 'o''clock'"`).
*
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
@@ -343,6 +347,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
<span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
<span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
<span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
<span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
<span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
</file>
<file name="protractor.js" type="protractor">
it('should format date', function() {
@@ -352,6 +358,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
});
</file>
</example>
+1 -1
View File
@@ -115,7 +115,7 @@
orderByFilter.$inject = ['$parse'];
function orderByFilter($parse){
return function(array, sortPredicate, reverseOrder) {
if (!isArray(array)) return array;
if (!(isArrayLike(array))) return array;
if (!sortPredicate) return array;
sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
sortPredicate = map(sortPredicate, function(predicate){
+1 -1
View File
@@ -1048,7 +1048,7 @@ function $HttpProvider() {
if (isObject(v)) {
if (isDate(v)){
v = v.toISOString();
} else if (isObject(v)) {
} else {
v = toJson(v);
}
}
+2 -2
View File
@@ -221,7 +221,7 @@ function $InterpolateProvider() {
* @description
* Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
*
* Use {@link ng.$interpolateProvider#startSymbol $interpolateProvider#startSymbol} to change
* Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
* the symbol.
*
* @returns {string} start symbol.
@@ -237,7 +237,7 @@ function $InterpolateProvider() {
* @description
* Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
*
* Use {@link ng.$interpolateProvider#endSymbol $interpolateProvider#endSymbol} to change
* Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
* the symbol.
*
* @returns {string} end symbol.
+11 -8
View File
@@ -320,17 +320,16 @@ LocationHashbangInHtml5Url.prototype =
* Change path, search and hash, when called with parameter and return `$location`.
*
* @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
* @param {string=} replace The path that will be changed
* @return {string} url
*/
url: function(url, replace) {
url: function(url) {
if (isUndefined(url))
return this.$$url;
var match = PATH_MATCH.exec(url);
if (match[1]) this.path(decodeURIComponent(match[1]));
if (match[2] || match[1]) this.search(match[3] || '');
this.hash(match[5] || '', replace);
this.hash(match[5] || '');
return this;
},
@@ -388,10 +387,11 @@ LocationHashbangInHtml5Url.prototype =
* Note: Path should always begin with forward slash (/), this method will add the forward slash
* if it is missing.
*
* @param {string=} path New path
* @param {(string|number)=} path New path
* @return {string} path
*/
path: locationGetterSetter('$$path', function(path) {
path = path ? path.toString() : '';
return path.charAt(0) == '/' ? path : '/' + path;
}),
@@ -427,7 +427,7 @@ LocationHashbangInHtml5Url.prototype =
* If the argument is a hash object containing an array of values, these values will be encoded
* as duplicate search parameters in the url.
*
* @param {(string|Array<string>|boolean)=} paramValue If `search` is a string, then `paramValue`
* @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
* will override only a single search property.
*
* If `paramValue` is an array, it will override the property of the `search` component of
@@ -446,7 +446,8 @@ LocationHashbangInHtml5Url.prototype =
case 0:
return this.$$search;
case 1:
if (isString(search)) {
if (isString(search) || isNumber(search)) {
search = search.toString();
this.$$search = parseKeyValue(search);
} else if (isObject(search)) {
// remove object undefined or null properties
@@ -483,10 +484,12 @@ LocationHashbangInHtml5Url.prototype =
*
* Change hash fragment when called with parameter and return `$location`.
*
* @param {string=} hash New hash fragment
* @param {(string|number)=} hash New hash fragment
* @return {string} hash
*/
hash: locationGetterSetter('$$hash', identity),
hash: locationGetterSetter('$$hash', function(hash) {
return hash ? hash.toString() : '';
}),
/**
* @ngdoc method
+4 -3
View File
@@ -753,7 +753,7 @@ Parser.prototype = {
var context = contextGetter ? contextGetter(scope, locals) : scope;
for (var i = 0; i < argsFn.length; i++) {
args.push(argsFn[i](scope, locals));
args.push(ensureSafeObject(argsFn[i](scope, locals), parser.text));
}
var fnPtr = fn(scope, locals, context) || noop;
@@ -841,13 +841,15 @@ Parser.prototype = {
//////////////////////////////////////////////////
function setter(obj, path, setValue, fullExp, options) {
ensureSafeObject(obj, fullExp);
//needed?
options = options || {};
var element = path.split('.'), key;
for (var i = 0; element.length > 1; i++) {
key = ensureSafeMemberName(element.shift(), fullExp);
var propertyObj = obj[key];
var propertyObj = ensureSafeObject(obj[key], fullExp);
if (!propertyObj) {
propertyObj = {};
obj[key] = propertyObj;
@@ -867,7 +869,6 @@ function setter(obj, path, setValue, fullExp, options) {
}
}
key = ensureSafeMemberName(element.shift(), fullExp);
ensureSafeObject(obj, fullExp);
ensureSafeObject(obj[key], fullExp);
obj[key] = setValue;
return setValue;
+22 -5
View File
@@ -139,10 +139,26 @@ function $RootScopeProvider(){
/**
* @ngdoc property
* @name $rootScope.Scope#$id
* @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for
* debugging.
*
* @description
* Unique scope ID (monotonically increasing) useful for debugging.
*/
/**
* @ngdoc property
* @name $rootScope.Scope#$parent
*
* @description
* Reference to the parent scope.
*/
/**
* @ngdoc property
* @name $rootScope.Scope#$root
*
* @description
* Reference to the root scope.
*/
Scope.prototype = {
constructor: Scope,
@@ -154,9 +170,8 @@ function $RootScopeProvider(){
* @description
* Creates a new child {@link ng.$rootScope.Scope scope}.
*
* The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and
* {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the
* scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
* The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
* The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
*
* {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
* desired for the scope and its child scopes to be permanently detached from the parent and
@@ -609,6 +624,8 @@ function $RootScopeProvider(){
logIdx, logMsg, asyncTask;
beginPhase('$digest');
// Check for changes to browser url that happened in sync before the call to $digest
$browser.$$checkUrlChange();
lastDirtyWatch = null;
+2
View File
@@ -56,6 +56,8 @@ angular.mock.$Browser = function() {
return listener;
};
self.$$checkUrlChange = angular.noop;
self.cookieHash = {};
self.lastCookieHash = {};
self.deferredFns = [];
+32 -4
View File
@@ -36,7 +36,7 @@ function MockWindow() {
};
this.location = {
href: 'http://server',
href: 'http://server/',
replace: noop
};
@@ -414,7 +414,7 @@ describe('browser', function() {
expect(replaceState).not.toHaveBeenCalled();
expect(locationReplace).not.toHaveBeenCalled();
expect(fakeWindow.location.href).toEqual('http://server');
expect(fakeWindow.location.href).toEqual('http://server/');
});
it('should use history.replaceState when available', function() {
@@ -426,7 +426,7 @@ describe('browser', function() {
expect(pushState).not.toHaveBeenCalled();
expect(locationReplace).not.toHaveBeenCalled();
expect(fakeWindow.location.href).toEqual('http://server');
expect(fakeWindow.location.href).toEqual('http://server/');
});
it('should set location.href when pushState not available', function() {
@@ -448,7 +448,7 @@ describe('browser', function() {
expect(pushState).not.toHaveBeenCalled();
expect(replaceState).not.toHaveBeenCalled();
expect(fakeWindow.location.href).toEqual('http://server');
expect(fakeWindow.location.href).toEqual('http://server/');
});
it('should return $browser to allow chaining', function() {
@@ -615,4 +615,32 @@ describe('browser', function() {
expect(browser.baseHref()).toEqual('/base/path/');
});
});
describe('integration tests with $location', function() {
beforeEach(module(function($provide, $locationProvider) {
spyOn(fakeWindow.history, 'pushState').andCallFake(function(stateObj, title, newUrl) {
fakeWindow.location.href = newUrl;
});
$provide.value('$browser', browser);
browser.pollFns = [];
$locationProvider.html5Mode(true);
}));
it('should update $location when it was changed outside of Angular in sync '+
'before $digest was called', function() {
inject(function($rootScope, $location) {
fakeWindow.history.pushState(null, '', 'http://server/someTestHash');
// Verify that infinite digest reported in #6976 no longer occurs
expect(function() {
$rootScope.$digest();
}).not.toThrow();
expect($location.path()).toBe('/someTestHash');
});
});
});
});
+8 -16
View File
@@ -621,10 +621,8 @@ describe('form animations', function() {
it('should trigger an animation when invalid', inject(function($animate) {
form.$setValidity('required', false);
assertValidAnimation($animate.queue[0], 'removeClass', 'ng-valid');
assertValidAnimation($animate.queue[1], 'addClass', 'ng-invalid');
assertValidAnimation($animate.queue[2], 'removeClass', 'ng-valid-required');
assertValidAnimation($animate.queue[3], 'addClass', 'ng-invalid-required');
assertValidAnimation($animate.queue[0], 'setClass', 'ng-invalid', 'ng-valid');
assertValidAnimation($animate.queue[1], 'setClass', 'ng-invalid-required', 'ng-valid-required');
}));
it('should trigger an animation when valid', inject(function($animate) {
@@ -634,10 +632,8 @@ describe('form animations', function() {
form.$setValidity('required', true);
assertValidAnimation($animate.queue[0], 'removeClass', 'ng-invalid');
assertValidAnimation($animate.queue[1], 'addClass', 'ng-valid');
assertValidAnimation($animate.queue[2], 'removeClass', 'ng-invalid-required');
assertValidAnimation($animate.queue[3], 'addClass', 'ng-valid-required');
assertValidAnimation($animate.queue[0], 'setClass', 'ng-valid', 'ng-invalid');
assertValidAnimation($animate.queue[1], 'setClass', 'ng-valid-required', 'ng-invalid-required');
}));
it('should trigger an animation when dirty', inject(function($animate) {
@@ -661,17 +657,13 @@ describe('form animations', function() {
it('should trigger custom errors as addClass/removeClass when invalid/valid', inject(function($animate) {
form.$setValidity('custom-error', false);
assertValidAnimation($animate.queue[0], 'removeClass', 'ng-valid');
assertValidAnimation($animate.queue[1], 'addClass', 'ng-invalid');
assertValidAnimation($animate.queue[2], 'removeClass', 'ng-valid-custom-error');
assertValidAnimation($animate.queue[3], 'addClass', 'ng-invalid-custom-error');
assertValidAnimation($animate.queue[0], 'setClass', 'ng-invalid', 'ng-valid');
assertValidAnimation($animate.queue[1], 'setClass', 'ng-invalid-custom-error', 'ng-valid-custom-error');
$animate.queue = [];
form.$setValidity('custom-error', true);
assertValidAnimation($animate.queue[0], 'removeClass', 'ng-invalid');
assertValidAnimation($animate.queue[1], 'addClass', 'ng-valid');
assertValidAnimation($animate.queue[2], 'removeClass', 'ng-invalid-custom-error');
assertValidAnimation($animate.queue[3], 'addClass', 'ng-valid-custom-error');
assertValidAnimation($animate.queue[0], 'setClass', 'ng-valid', 'ng-invalid');
assertValidAnimation($animate.queue[1], 'setClass', 'ng-valid-custom-error', 'ng-invalid-custom-error');
}));
});
+21 -10
View File
@@ -580,19 +580,30 @@ describe('input', function() {
});
if (!_jqLiteMode) {
it('should not cause the double $digest when triggering an event using jQuery', function() {
$sniffer.hasEvent = function(eventName) {
return eventName !== 'input';
};
describe('double $digest when triggering an event using jQuery', function() {
function run() {
$sniffer.hasEvent = function(eventName) {
return eventName !== 'input';
};
compileInput('<input type="text" ng-model="name" name="alias" ng-change="change()" />');
compileInput('<input type="text" ng-model="name" name="alias" ng-change="change()" />');
scope.field = 'fake field';
scope.$watch('field', function() {
// We need to use _originalTrigger since trigger is modified by Angular Scenario.
inputElm._originalTrigger('change');
scope.field = 'fake field';
scope.$watch('field', function() {
// We need to use _originalTrigger since trigger is modified by Angular Scenario.
inputElm._originalTrigger('change');
});
scope.$apply();
}
it('should not cause the double $digest with non isolate scopes', function() {
run();
});
it('should not cause the double $digest with isolate scopes', function() {
scope = scope.$new(true);
run();
});
scope.$apply();
});
}
});
+84
View File
@@ -39,4 +39,88 @@ describe('event directives', function() {
expect($rootScope.formSubmitted).toEqual('foo');
}));
});
describe('focus', function() {
describe('call the listener asynchronously during $apply', function() {
function run(scope) {
inject(function($compile) {
element = $compile('<input type="text" ng-focus="focus()">')(scope);
scope.focus = jasmine.createSpy('focus');
scope.$apply(function() {
element.triggerHandler('focus');
expect(scope.focus).not.toHaveBeenCalled();
});
expect(scope.focus).toHaveBeenCalledOnce();
});
}
it('should call the listener with non isolate scopes', inject(function($rootScope) {
run($rootScope.$new());
}));
it('should call the listener with isolate scopes', inject(function($rootScope) {
run($rootScope.$new(true));
}));
});
it('should call the listener synchronously inside of $apply if outside of $apply',
inject(function($rootScope, $compile) {
element = $compile('<input type="text" ng-focus="focus()" ng-model="value">')($rootScope);
$rootScope.focus = jasmine.createSpy('focus').andCallFake(function() {
$rootScope.value = 'newValue';
});
element.triggerHandler('focus');
expect($rootScope.focus).toHaveBeenCalledOnce();
expect(element.val()).toBe('newValue');
}));
});
describe('blur', function() {
describe('call the listener asynchronously during $apply', function() {
function run(scope) {
inject(function($compile) {
element = $compile('<input type="text" ng-blur="blur()">')(scope);
scope.blur = jasmine.createSpy('blur');
scope.$apply(function() {
element.triggerHandler('blur');
expect(scope.blur).not.toHaveBeenCalled();
});
expect(scope.blur).toHaveBeenCalledOnce();
});
}
it('should call the listener with non isolate scopes', inject(function($rootScope) {
run($rootScope.$new());
}));
it('should call the listener with isolate scopes', inject(function($rootScope) {
run($rootScope.$new(true));
}));
});
it('should call the listener synchronously inside of $apply if outside of $apply',
inject(function($rootScope, $compile) {
element = $compile('<input type="text" ng-blur="blur()" ng-model="value">')($rootScope);
$rootScope.blur = jasmine.createSpy('blur').andCallFake(function() {
$rootScope.value = 'newValue';
});
element.triggerHandler('blur');
expect($rootScope.blur).toHaveBeenCalledOnce();
expect(element.val()).toBe('newValue');
}));
});
});
-18
View File
@@ -34,23 +34,5 @@ describe('ngKeyup and ngKeydown directives', function() {
expect($rootScope.touched).toEqual(true);
}));
it('should get called on focus', inject(function($rootScope, $compile) {
element = $compile('<input ng-focus="touched = true">')($rootScope);
$rootScope.$digest();
expect($rootScope.touched).toBeFalsy();
browserTrigger(element, 'focus');
expect($rootScope.touched).toEqual(true);
}));
it('should get called on blur', inject(function($rootScope, $compile) {
element = $compile('<input ng-blur="touched = true">')($rootScope);
$rootScope.$digest();
expect($rootScope.touched).toBeFalsy();
browserTrigger(element, 'blur');
expect($rootScope.touched).toEqual(true);
}));
});
+4 -4
View File
@@ -939,8 +939,8 @@ describe('ngRepeat', function() {
it('should throw error on adding existing duplicates and recover', function() {
scope.items = [a, a, a];
scope.$digest();
expect($exceptionHandler.errors.shift().message).
toMatch(/^\[ngRepeat:dupes\] Duplicates in a repeater are not allowed\. Use 'track by' expression to specify unique keys\. Repeater: item in items, Duplicate key: object:003/);
expect($exceptionHandler.errors.shift().message).toMatch(
/^\[ngRepeat:dupes\] Duplicates in a repeater are not allowed\. Use 'track by' expression to specify unique keys\. Repeater: item in items, Duplicate key: object:003, Duplicate value: {}/);
// recover
scope.items = [a];
@@ -959,8 +959,8 @@ describe('ngRepeat', function() {
it('should throw error on new duplicates and recover', function() {
scope.items = [d, d, d];
scope.$digest();
expect($exceptionHandler.errors.shift().message).
toMatch(/^\[ngRepeat:dupes\] Duplicates in a repeater are not allowed\. Use 'track by' expression to specify unique keys\. Repeater: item in items, Duplicate key: object:009/);
expect($exceptionHandler.errors.shift().message).toMatch(
/^\[ngRepeat:dupes\] Duplicates in a repeater are not allowed\. Use 'track by' expression to specify unique keys\. Repeater: item in items, Duplicate key: object:009, Duplicate value: {}/);
// recover
scope.items = [a];
+6 -1
View File
@@ -83,6 +83,11 @@ describe('filters', function() {
num = formatNumber(123.1, pattern, ',', '.', 3);
expect(num).toBe('123.100');
});
it('should format numbers that round to zero as nonnegative', function(){
var num = formatNumber(-0.01, pattern, ',', '.', 1);
expect(num).toBe('0.0');
});
});
describe('currency', function() {
@@ -176,7 +181,7 @@ describe('filters', function() {
expect(number(1e-6, 6)).toEqual('0.000001');
expect(number(1e-7, 6)).toEqual('0.000000');
expect(number(-1e-50, 0)).toEqual('-0');
expect(number(-1e-50, 0)).toEqual('0');
expect(number(-1e-6, 6)).toEqual('-0.000001');
expect(number(-1e-7, 6)).toEqual('-0.000000');
});
+10
View File
@@ -17,6 +17,16 @@ describe('Filter: orderBy', function() {
expect(orderBy([{a:15}, {a:2}], 'a', "reverse")).toEqualData([{a:15}, {a:2}]);
});
it('should sort inherited from array', function(){
function BaseCollection(){}
BaseCollection.prototype = Array.prototype;
var child = new BaseCollection();
child.push({a:2});
child.push({a:15});
expect(orderBy(child, 'a', true)).toEqualData([{a:15}, {a:2}]);
});
it('should sort array by predicate', function() {
expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['a', 'b'])).toEqualData([{a:2, b:1}, {a:15, b:1}]);
expect(orderBy([{a:15, b:1}, {a:2, b:1}], ['b', 'a'])).toEqualData([{a:2, b:1}, {a:15, b:1}]);
+36
View File
@@ -87,6 +87,19 @@ describe('$location', function() {
expect(url.absUrl()).toBe('http://www.domain.com:9877/new/path?search=a&b=c&d#hash');
});
it('path() should not break on numeric values', function() {
url.path(1);
expect(url.path()).toBe('/1');
expect(url.absUrl()).toBe('http://www.domain.com:9877/1?search=a&b=c&d#hash');
});
it('path() should set to empty path on null value', function () {
url.path('/foo');
expect(url.path()).toBe('/foo');
url.path(null);
expect(url.path()).toBe('/');
});
it('search() should accept string', function() {
url.search('x=y&c');
@@ -127,6 +140,13 @@ describe('$location', function() {
});
it('search() should accept numeric keys', function() {
url.search({1: 'one', 2: 'two'});
expect(url.search()).toEqual({'1': 'one', '2': 'two'});
expect(url.absUrl()).toBe('http://www.domain.com:9877/path/b?1=one&2=two#hash');
});
it('search() should handle multiple value', function() {
url.search('a&b');
expect(url.search()).toEqual({a: true, b: true});
@@ -143,6 +163,8 @@ describe('$location', function() {
it('search() should handle single value', function() {
url.search('ignore');
expect(url.search()).toEqual({ignore: true});
url.search(1);
expect(url.search()).toEqual({1: true});
});
@@ -163,6 +185,20 @@ describe('$location', function() {
});
it('hash() should accept numeric parameter', function() {
url.hash(5);
expect(url.hash()).toBe('5');
expect(url.absUrl()).toBe('http://www.domain.com:9877/path/b?search=a&b=c&d#5');
});
it('hash() should accept null parameter', function() {
url.hash(null);
expect(url.hash()).toBe('');
expect(url.absUrl()).toBe('http://www.domain.com:9877/path/b?search=a&b=c&d');
});
it('url() should change the path, search and hash', function() {
url.url('/some/path?a=b&c=d#hhh');
expect(url.url()).toBe('/some/path?a=b&c=d#hhh');
+37
View File
@@ -1006,6 +1006,43 @@ describe('parser', function() {
expect(scope.$eval('fn().anotherFn()')).toBe(true);
});
it('should disallow traversing the Function object in a setter: E02', function() {
expect(function() {
// This expression by itself isn't dangerous. However, one can use this to
// automatically call an object (e.g. a Function object) when it is automatically
// toString'd/valueOf'd by setting the RHS to Function.prototype.call.
scope.$eval('hasOwnProperty.constructor.prototype.valueOf = 1');
}).toThrowMinErr(
'$parse', 'isecfn', 'Referencing Function in Angular expressions is disallowed! ' +
'Expression: hasOwnProperty.constructor.prototype.valueOf = 1');
});
it('should disallow passing the Function object as a parameter: E03', function() {
expect(function() {
// This expression constructs a function but does not execute it. It does lead the
// way to execute it if one can get the toString/valueOf of it to call the function.
scope.$eval('["a", "alert(1)"].sort(hasOwnProperty.constructor)');
}).toThrow();
});
it('should prevent exploit E01', function() {
// This is a tracking exploit. The two individual tests, it('should … : E02') and
// it('should … : E03') test for two parts to block this exploit. This exploit works
// as follows:
//
// • Array.sort takes a comparison function and passes it 2 parameters to compare. If
// the result is non-primitive, sort then invokes valueOf() on the result.
// • The Function object conveniently accepts two string arguments so we can use this
// to construct a function. However, this doesn't do much unless we can execute it.
// • We set the valueOf property on Function.prototype to Function.prototype.call.
// This causes the function that we constructed to be executed when sort calls
// .valueOf() on the result of the comparison.
expect(function() {
scope.$eval('' +
'hasOwnProperty.constructor.prototype.valueOf=valueOf.call;' +
'["a","alert(1)"].sort(hasOwnProperty.constructor)');
}).toThrow();
});
it('should call the function once when it is part of the context', function() {
var count = 0;