Compare commits

...

119 Commits

Author SHA1 Message Date
Vojta Jina a9b5a1087d chore(CHANGELOG): add notes for 1.2.15 2014-03-21 14:58:48 -07:00
Vojta Jina 87b18b9fbe docs(changelog): remove 1.3 notes from 1.2 2014-03-21 14:57:43 -07:00
Caitlin Potter ad128e09ff test($rootScope): add assertion to test ensuring that NaN -> NaN does not throw
https://github.com/angular/angular.js/commit/fb6062fb9d83545730b993e94ac7482ffd43a62c implements a
fix for NaN values causing $watchCollection to throw an infdig error. This change updates the test
by adding an assertion which explains what is actually being tested a bit better, and may also
provide better information in the event that the test ever fails.

Closes #6758
2014-03-21 13:05:29 -07:00
alexgarrett 187b4adbd2 docs(tutorial): correct spelling mistake 2014-03-21 13:05:29 -07:00
Trevor Ewen 375c47d0c0 docs($document): add a documentation example.
The $document docs are pretty empty, and this fills them out a bit. The example itself may not be
particularly useful, but it could be improved or removed later. Works for me.

Closes #6757
2014-03-21 13:05:29 -07:00
thorn0 8fd47a1cd5 docs($q): add mention of Antroid 2.x browser
The Android 2.x browser is not ES5-compatible in that it does not allow
use of reserved words as property names. This docs fix adds Android to the
note to the `$q` docs which already make it known that string property
notation should be used when using the `finally` method on `$q`.
2014-03-21 13:05:29 -07:00
Sekib Omazic e48c28fe92 fix($rootScope): ng-repeat can't handle NaN values. #4605
$watchCollection checks if oldValue !== newValue which does not work for NaN. This was causing
infinite digest errors, since comparing NaN to NaN in $watchCollection would always return false,
indicating that a change was occuring on each loop.

This fix adds a simple check to see if the current value and previous value are both NaN, and
if so, does not count it as a change.

Closes #4605
2014-03-21 13:05:29 -07:00
Caitlin Potter 10d3e1e447 fix(orderBy): support string predicates containing non-ident characters
The orderBy filter now allows string predicates passed to the orderBy filter to make use property
name predicates containing non-ident strings, such as spaces or percent signs, or non-latin
characters.

This behaviour requires the predicate string to be double-quoted.

In markup, this might look like so:

```html
<div ng-repeat="item in items | orderBy:'\"Tip %\"'">
...
</div>
```

Or in JS:

```js
var sorted = $filter('orderBy')(array, ['"Tip %"', '-"Subtotal $"'], false);
```

Closes #6143
Closes #6144
2014-03-21 13:05:29 -07:00
Caitlin Potter 93d1c95c61 fix(ngCookie): convert non-string values to string
Previously, non-string values stored in $cookies would be removed, without warning the user, and
causing difficulty debugging. Now, the value is converted to string before being stored, and the
value is not dropped. Serialization may be customized using the toString() method of an object's
prototype.

Closes #6151
Closes #6220
2014-03-21 13:05:29 -07:00
Chris Constantin 01a34f513b fix(ngTouch): update workaround for desktop Webkit quirk
Fix click busting of input click triggered by a label click quickly
following a touch event on a different element, in desktop
and mobile WebKit

To reproduce the issue fixed by this commit set up a page with
 - an element with ng-click
 - a radio button (with hg-model) and associated label
In a quick sequence tap on the element and then on the label.
The radio button will not be checked, unless PREVENT_DURATION has passed

Closes #6302
2014-03-21 13:05:29 -07:00
frandroid 916e53ce14 docs(tutorial/step_05): fix services link 2014-03-21 11:42:19 -07:00
frandroid b91b3119a4 docs(tutorial/step_05): removed stray "a" 2014-03-21 11:42:18 -07:00
Siddique Hameed 0d60f8d367 fix(angular.bootstrap): only allow angular to load once
This is hard to test as a unit-test, since it involves the actual loading
of angular, but it turns out that it is easy to test using a protractor
e2e test.

Closes #5863
Closes #5587
2014-03-21 11:42:18 -07:00
Peter Bacon Darwin ca69dc6f17 chore(utils): fix version number processing
The changes to version-info meant that the version being injected into
the code at build time was missing the "dot" (patch) version and the
release code-name.
2014-03-21 11:42:18 -07:00
Caitlin Potter 5b7f1bcc00 style($templateCache): remove trailing whitespace
This was introduced by 2ca6d650e8, somewhat inexplicably as I had run
grunt ci-checks locally. But regardless, this should fix this up.
2014-03-21 11:42:18 -07:00
Jesse Palmer 9ab594a66c docs($templateCache): use GFM example format rather than <pre> tags
Updated example formatting.

Closes #6068
2014-03-21 11:42:18 -07:00
Edward Brey 6c82a497c6 docs(loader): add annotations to example 2014-03-21 11:42:18 -07:00
Tyler Kellogg df804406fb docs($cookies): cookies serializer only supports strings
Closes #6705
2014-03-21 11:42:18 -07:00
poshest dadce485a7 docs(errors/$compile/nonassing): update nonassign.ngdoc
It might seem obvious that if you don't supply "bind" attribute in this case, you'll get an error,
but I feel this is worth adding to the doc.

Closes #6725
2014-03-21 11:42:18 -07:00
Sekib Omazic 344cdce136 docs(css): RegExp doesn't have .type-hint-regexp class
type-hint-regexp gets a nice color

closes #6596
2014-03-21 11:42:18 -07:00
Gias Kay Lee f0347d5efa docs(module): add link to mentioned resource
Closes #6628
2014-03-21 11:42:18 -07:00
Gias Kay Lee 1c27e5fc37 docs(css): Fix word breaks issue in <pre>
Closes #6586
2014-03-21 11:42:17 -07:00
wbyoko 5fb298b90f docs(migration): note that services can now return functions
This change mostly effects preprocessed javascript.
2014-03-21 11:42:17 -07:00
Takashi Nakagawa 483325a7b5 chore(formatting): removed unnecessary white spaces 2014-03-21 11:42:17 -07:00
unicodesnowman a86cb7d794 docs(ngView): remove global controller definitions
instead use angular modules
also fix formatting
2014-03-21 11:42:17 -07:00
Neil Johnston c7e60153a5 docs(tutorial/step_02): add experiment to update controller test
Add an experiment to update the controller unit test after modifying it
with the new model property.
2014-03-21 11:42:17 -07:00
Sekib Omazic c0416866f5 docs(booleanAttrs): fix typo 2014-03-21 11:42:17 -07:00
Jan Hancic 8ba452544e docs(tutorial/step_12): link to API docs 2014-03-21 11:42:17 -07:00
David Rogers 8f7f0d26ed docs(ngForm): remove duplicate @param annotation
When the example for `ngAnimate` was added in commit:3344396, the `@param name` annotation was unintentionally duplicated. Remove this duplicate.

Closes #6720
2014-03-21 11:42:17 -07:00
Caitlin Potter 98d825e10d fix(jqLite): traverse host property for DocumentFragment in inheritedData()
If dealing with a document fragment node with a host element, and no parent, use the host
element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
to lookup parent controllers.

Closes #6637
2014-03-21 11:42:17 -07:00
Mark Jones 57b0d91fd8 docs(ngInclude): make the quote type explicit 2014-03-21 11:42:17 -07:00
linclark 9226b36572 docs($http): update shortcut method description
Update docs to reflect that $http no longer requires passing in an HTTP method, as changed in #6401.
2014-03-21 11:42:16 -07:00
bradwheel 39635fd0d7 docs(ngRoute): remove global controller syntax in the example 2014-03-21 11:42:16 -07:00
Igor Minar cad307fa1f docs(triaging): correct information about milestones 2014-03-21 11:42:16 -07:00
Denis Parchenko 78bc84c497 docs(guide/module): remove duplicate word
Closes #6709
2014-03-21 11:42:16 -07:00
Peter Bacon Darwin 1f2750136e docs(runnableExamples): add "edit in Plunker" button
The "runnableExample.template.html" template overrides the one in the
dgeni-packages "examples" package with a similar template that also has
a link to a special Plunker URL that can pull in the example from our
code.angularjs.org website.
2014-03-21 11:42:16 -07:00
Peter Bacon Darwin 5b93e5fcfc chore(shrinkwrap): grunt-jasmine-node is retrieved from github 2014-03-21 11:42:16 -07:00
Caitlin Potter 770fd5a917 docs(misc/contribute): make anchor links work properly
Closes #6706
2014-03-21 11:42:16 -07:00
Brian Andersen 4b29186696 docs(tutorial): fix broken link
On page http://docs.angularjs.org/tutorial/step_05 link is broken.

Should point to http://docs.angularjs.org/guide/services NOT http://docs.angularjs.org/guide/dev_guide.services

Closes #6714
2014-03-21 11:42:16 -07:00
Tobias Bosch 7b5be9ee29 chore(CHANGELOG.md): add changelog for 1.3.0-beta.3 2014-03-21 11:33:17 -07:00
Jeff Cross eeb261bcd5 chore: update changelog for 1.3.0-beta.2 2014-03-21 11:33:05 -07:00
Tobias Bosch ef88a8a020 chore(CHANGELOG.md): add input type date PR as breaking change
Related to #6630
2014-03-21 11:32:54 -07:00
Tobias Bosch 86ab885fd9 chore(release): fix angularjs.org cdn script 2014-03-20 14:33:14 -07:00
Peter Bacon Darwin de07ddeac6 chore(angularjs.org/publish.sh): align release script with new website
Closes #6690
2014-03-20 14:33:14 -07:00
Jeff Balboni dc149de936 fix(select): avoid checking option element selected properties in render
In Firefox, hovering over an option in an open select menu updates the selected property of option
elements. This means that when a render is triggered by the digest cycle, and the list of options
is being rendered, the selected properties are reset to the values from the model and the option
hovered over changes. This fix changes the code to only use DOM elements' selected properties in a
comparison when a change event has been fired. Otherwise, the internal new and existing option
arrays are used.

Closes #2448
Closes #5994
Closes #6769
2014-03-20 17:27:02 -04:00
Vojta Jina 320f6d1214 chore(scripts): fix the versions script again 2014-03-20 14:03:55 -07:00
Vojta Jina 1517d6d2f2 chore(scripts): fix the versions script 2014-03-20 14:03:49 -07:00
Vojta Jina 6bb17af2e3 chore(scripts): disable testing seed and phonecat during a release
This reverts commit d5294ebfa0.

It turned out to be more work and I don't wanna deal with it right now.
2014-03-20 14:03:41 -07:00
Vojta Jina 505ead7e58 chore(scripts): test seed and phonecat during a release 2014-03-20 14:03:35 -07:00
Vojta Jina 1da4e89385 chore(scripts): make the release script more flexible
Now the SHA can be short/long, whateva.
2014-03-20 14:03:28 -07:00
Chirayu Krishnappa 83f37d78ba fix(version-info): explicitly specify the remote
`git ls-remote --tags` assumes that you have a remote set up for your
current branch.  That isn't the case, at least for me, when I'm working
on local branches.  `grunt write` doesn't do the right thing in that
case (`git ls-remote --tags` bails out and the silent: true param makes
this a pain to debug.)  Prefer explicit to implicit.

Closes #6678.
2014-03-20 13:58:44 -07:00
Brett Porter d4ac25496a test(ngMock): workaround issue with negative timestamps
In some specific timezones and operating systems, it seems that
getTimezoneOffset() can return an incorrect value for negative timestamps, as
described in #5017. While this isn't something easily fixed in the mock code,
the tests can avoid that particular timeframe by using a positive timestamp.

Closes #5017
Closes #6730
2014-03-18 21:31:20 -07:00
Traxmaxx e84da2283c fix($$RAFProvider): check for webkitCancelRequestAnimationFrame
Android 4.3 only supports webkitCancelRequestAnimationFrame.

Closes #6526
2014-03-18 21:38:20 -04:00
Igor Minar 3dd9572754 fix(Scope): $watchCollection should call listener with oldValue
Originally we destroyed the oldValue by incrementaly copying over portions of the newValue
into the oldValue during dirty-checking, this resulted in oldValue to be equal to newValue
by the time we called the watchCollection listener.

The fix creates a copy of the newValue each time a change is detected and then uses that
copy *the next time* a change is detected.

To make `$watchCollection` behave the same way as `$watch`, during the first iteration
the listener is called with newValue and oldValue being identical.

Since many of the corner-cases are already covered by existing tests, I refactored the
test logging to include oldValue and made the tests more readable.

Closes #2621
Closes #5661
Closes #5688
Closes #6736
2014-03-18 12:01:42 -07:00
Igor Minar 922cb7e42f chore(log): add log.empty() method to the testing logger
`log.empty()` is the same as `log.reset()`, except thati `empty()`  also returns the current array with messages

instead of:

```
// do work
expect(log).toEqual(['bar']);
log.reset();
```

do:

```
// do work
expect(log.empty()).toEqual(['bar']);
```
2014-03-18 12:01:35 -07:00
Peter Bacon Darwin 103cb513d9 docs(guide/concepts): move ng-app into example text
Closes #6639
2014-03-18 07:14:06 +00:00
Peter Bacon Darwin c24e4e4ed5 chore(package.json): update dgeni-packages dependency 2014-03-18 07:14:06 +00:00
Peter Bacon Darwin 1b46a7dcdf chore(version-info): previousVersions should not return undefined
Closes #6702
2014-03-18 07:13:43 +00:00
Emile Silvis 8d28d65b36 docs(guide/tutorial): make capitalization of "Angular" consistent
- step_05.ngdoc
- step_06.ngdoc
- step_07.ngdoc
- step_08.ngdoc

Closes #6686
Closes #6687
Closes #6688
Closes #6689
2014-03-16 09:52:51 -07:00
Bruno Baia fbb125a3af fix($http): allow sending Blob data using $http
Closes #5012
2014-03-16 09:52:44 -07:00
Igor Minar ca7336391a chore(package.json): update karma to 0.12.0 2014-03-16 09:52:14 -07:00
Peter Bacon Darwin 771bccc35c chore(clean-shrinkwrap): add a utility to clean up the shrinkwrap file
This is to deal with https://github.com/npm/npm/issues/3581

See the previous commit for more info.

Closes #6672
2014-03-16 09:52:04 -07:00
Igor Minar c794b96bdc chore(npm): clean up shrinkwrap file, remove unused properties
from our experiements it appears that the presense or absense of the from and resolved properties
makes no difference on the behavior of  but  updates these properties
with different values depending on different state of the cache and node_modules.

So in order to get clean diffs during updates, we are just going to drop these properties and have
a script to do this automatically.

Long term this should be fixed in npm: https://github.com/npm/npm/issues/3581
2014-03-16 09:51:42 -07:00
Pawel Kozlowski f108a2a994 fix($http): don't covert 0 status codes to 404 for non-file protocols
PR #5547 introduced conversion of all 0 status codes to 404 for cases
where no response was recieved (previously this was done for the
file:// protocol only). But this mechanism is too eager and
masks legitimate cases where status 0 should be returned. This commits
reverts to the previous mechanism of handling 0 status code for the
file:// protocol (converting 0 to 404) while retaining the returned
status code 0 for all the protocols other than file://

Fixes #6074
Fixes #6155
2014-03-14 13:45:07 -07:00
Nick Heiner 7cbf61cabb docs(ngMock): grammar nitpick. 2014-03-14 11:50:48 -07:00
Sagie Maoz dfdb72559f docs(guide/compiler): add missing closing parenthesis 2014-03-14 11:50:37 -07:00
Nick Carter d69793d93c docs(guide/unit-testing): fix typo 2014-03-14 11:50:28 -07:00
Wesley Cho b068c8b605 docs($resource): fix example using promise 2014-03-14 11:50:19 -07:00
Thomas Belin ec16352579 fix (ngAnimate): fix requestAnimationFrame for old version of Firefox
The recent $$RAFProvider which is a wrapper for the native
requestAnimationFrame method doesn't use the mozRequestAnimationFrame.
Old versions of FF (20 for example) crash if ngAnimate is included

No breaking changes and fix issue https://github.com/angular/angular.js/issues/6535

Closes #6535
Closes #6540
2014-03-14 11:50:05 -07:00
Peter Bacon Darwin aa4ba23350 chore(doc-gen): fix dependencyPath 2014-03-14 11:49:01 -07:00
Peter Bacon Darwin 25e639b474 chore(package.json): update dgeni-packages dependency
The new version of dgeni-packages/ngdoc generates a manifest for each
example that can be used by plunker.
2014-03-14 11:48:07 -07:00
Matias Niemelä ee8e4a946e fix($$rAF): always fallback to a $timeout incase native rAF isn't supported
Closes #6654
2014-03-14 13:48:37 -04:00
Tomer Chachamu a41a2a1d2c fix(ngAnimate): setting classNameFilter disables animation inside ng-if
Closes #6539
2014-03-14 13:48:29 -04:00
Peter Bacon Darwin eadd8d08d3 docs(scripts/utils.inc): clarify documentation 2014-03-14 11:12:13 +00:00
Peter Bacon Darwin 602a1142e8 chore(shrinkwrap): update dgeni-packages 2014-03-13 20:31:44 +00:00
Peter Bacon Darwin 0b7fef3d94 chore(shrinkwrap): re-run shrinkwrap locally
This will make the following commit clearer when the update is run.
2014-03-13 20:31:44 +00:00
Tobias Bosch 809d47ec77 chore(version-info): use remote tags and increment patch version 2014-03-12 17:33:52 -07:00
Peter Bacon Darwin f3444d495d chore(version-info): better error msg if not tags 2014-03-12 17:33:41 -07:00
Igor Minar 612c882b83 chore(npm): add shrinkwrap to lock down dependencies
We need to be able to build angular at older shas, without the lock file / shrinkwrap file
the dependencies will resolve differently on different machines and at different times.

This will help us avoid broken builds and hard to track down issues.

I had to manually edit this file after it was generated because `npm shrinkwrap` will install
optional dependencies as if they were hard dependencies.

See: https://github.com/npm/npm/issues/2679#issuecomment-37361236

My manual edit:

```
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index 756df44..dc157eb 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -3110,19 +3110,7 @@
         "chokidar": {
           "version": "0.8.1",
           "from": "https://registry.npmjs.org/chokidar/-/chokidar-0.8.1.tgz",
-          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-0.8.1.tgz",
-          "dependencies": {
-            "fsevents": {
-              "version": "0.1.6",
-              "from": "fsevents@0.1.6",
-              "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-0.1.6.tgz"
-            },
-            "recursive-readdir": {
-              "version": "0.0.2",
-              "from": "recursive-readdir@0.0.2",
-              "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-0.0.2.tgz"
-            }
-          }
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-0.8.1.tgz"
         },
         "glob": {
           "version": "3.2.9",
```

Additionally chokidar doesn't list the dependencies above as optional, but that will hopefully
be soon fixed: https://github.com/paulmillr/chokidar/pull/106

In the meantime the patch from the PR above needs to be applied to
node_modules/karma/node_modules/chokidar/package.json before running `npm shrinkwrap`

----

After this change is applied, angular core developers don't need to do anything differently,
except when updating dependencies we need to call `npm update && npm shrinkwrap --dev`
followed by reappling my patch above until npm's bug.

Closes #6653
2014-03-11 22:46:54 -07:00
Igor Minar f2a6be3129 chore(build): don't instruct Jenkins test on IE
for an unknown reason the VMs can't connect to local karma, so all builds on Jenkins (ci.angularjs.org)
are failing right now.

Since we want to kill Jenkins anyway, and travis tests on IE, this should not have any
significant impact on us.

Conflicts:
	jenkins_build.sh
2014-03-11 10:43:36 -07:00
Louis Haußknecht 465663ed77 docs(route.js): changed html entities lt gt to < and > 2014-03-11 10:40:57 -07:00
Basem Mostafa 1102ffaaf8 docs(ngRepeat): Separate animation class in new lines
Moving to new lines & making it bold to avoid confusion
when they r all in same line without any separation

Closes #6633
2014-03-11 10:40:51 -07:00
Matias Niemelä 98f6a82390 chore(docs): ensure the "Improve this doc" button is clickable
Closes #6631
2014-03-11 10:40:45 -07:00
doodeec a43c6e1828 docs($route): change routes property to correct type
change $route.routes property type to Object, property is marked incorrectly as an Array

Closes #6552
2014-03-11 10:40:39 -07:00
Narretz 0db301f863 docs(guide/forms): remove unnecessary controller reference
the controller reference was breaking the custom validation example

Closes #6525
Closes #6533
2014-03-11 10:40:32 -07:00
chadfennell 8e6d3875c6 docs(guide/providers): remove unneeded word "the"
no need to specify which space, there's only one :)

Closes #6622
2014-03-11 10:40:21 -07:00
Chung-Min Cheng b9d77d46ff docs(tutorial/step-12): correct application name
Fixed wrong app name:
- phonecat -> phonecatApp, which meets the code in app.js

Closes #6611
2014-03-11 10:40:12 -07:00
Peter Bacon Darwin 96f94d4347 docs(Error404): better heading 2014-03-11 10:40:05 -07:00
Peter Bacon Darwin 63831f118f docs(Error404): improve search results layout 2014-03-11 10:39:59 -07:00
Peter Bacon Darwin ebe280eede docs(404 errors): provide a better 404 experience
It is a bit rough and ready but does a better job than nothing.
2014-03-11 10:39:47 -07:00
Brian Ford 95d6cdc7c7 docs(changelog): release notes for 1.3.0-beta.1 retractable-eyebrow 2014-03-11 10:36:08 -07:00
Sekib Omazic 9223215add docs($sce): correct typo
`consititute` -> `constitute`

Typo fixed

Closes #6607
2014-03-11 10:35:59 -07:00
Igor Minar 309cfd109f chore(build): upgrade grunt-jscs-checker to ~0.4.0
this is primarily to resolve peerdependency version mismatch issue
2014-03-11 10:35:51 -07:00
Sekib Omazic cfc6175aab docs(ngBind): fix typo
`preferrable` -> `preferable`

Typo fixed

Closes #6606
2014-03-11 10:35:42 -07:00
Igor Minar dc39f368c3 docs(guide/migration): fix broken link 2014-03-11 10:35:34 -07:00
Sekib Omazic c0f3400573 docs(guide/migration): fix typos
A few typos fixed.

Closes #6605
2014-03-11 10:35:27 -07:00
Sekib Omazic 822d7e5ae9 docs(guide/directive): fix typo
`restictions` -> `restrictions`

Closes #6604
2014-03-11 10:35:18 -07:00
Stéphane Reynaud 5874db84ab docs(tutorial): display button icons (Previous, Live Demo, ...)
In relation to https://github.com/angular/dgeni-packages/pull/8

Closes #6641
2014-03-11 10:21:32 +00:00
Peter Bacon Darwin e9e8d49216 style(jsdoc tags): remove/ammend invalid tags
As highlighted by the new sterner dgeni.
2014-03-11 10:16:38 +00:00
Peter Bacon Darwin 550fc21ce5 chore(build): refactor build version information 2014-03-11 06:54:52 +00:00
Peter Bacon Darwin a8aba8957b docs(versions): rework the version info extraction
The docs were relying on the grunt/util module for getting version info
but this was unreliable and full of custom regexes.  This is moved into
a new version-info module that makes much better use of the semver library.
2014-03-11 06:54:52 +00:00
Lucas Galfasó ca0ac64997 fix($compile): support templates with thead and tfoot root elements
If the first element in a template is a <thead> or a <tfoot>, then
use the existing logic to handle table elements compilation.

Closes #6289
2014-03-07 15:09:16 -08:00
Peter Bacon Darwin 7678501bc9 chore(package): update dgeni dependencies 2014-03-07 15:09:11 -08:00
Peter Bacon Darwin dec5eb6e83 chore(doc-gen): add contentFolder config property 2014-03-07 15:09:06 -08:00
Peter Bacon Darwin 2eff326781 chore(doc-gen): add inline @type tag 2014-03-07 15:09:01 -08:00
Peter Bacon Darwin 50ce5746a7 docs($route): fix formatting of example code 2014-03-07 15:08:55 -08:00
Peter Bacon Darwin 1537f80267 chore(doc-gen): fix error-doc processor
The meta-data should be parsed from the name not the id.
2014-03-07 15:08:50 -08:00
Peter Bacon Darwin 021d3aa21a chore(doc-gen): improve error reporting 2014-03-07 15:08:42 -08:00
Eddie Hedges e8c8c5459e docs(tutorial): link update for Jasmine
Jasmine doesn't live at the replaced link anymore.
It has a link to click through, but I figured it would be better
to just go directly to the correct location.

Closes #6591
2014-03-07 15:08:37 -08:00
Misha Moroshko 1c20aed318 docs(guide/services): minor fixes 2014-03-07 15:08:16 -08:00
Chirayu Krishnappa 4c4d24a338 chore(publish.sh): publish to all serving backends 2014-03-07 15:05:30 -08:00
Timothée Jeannin 8c7b9b8de4 style: enable jscs requireLeftStickedOperators rule
Closed #6544.
2014-03-07 15:05:22 -08:00
Tony Bergeron f39ac571c4 docs(directive.ngdoc): typo fix 2014-03-07 15:04:44 -08:00
tpiere 84f36701bc docs(tutorial): update step_09.ngdoc
Closes #5991
2014-03-07 15:04:37 -08:00
Zak Johnson 6d4ce240de docs(guide/services): clean up typos 2014-03-07 15:04:30 -08:00
mgerstenblatt 229a155aef docs(guide/forms): fix a typo
Closes #6556
2014-03-07 15:04:24 -08:00
Chung-Min Cheng 73250089cd docs(tutorial): update step_08.ngdoc
Closes #6537
2014-03-07 15:04:16 -08:00
Sharon DiOrio a72bc4e69f docs(tutorial/index): improve accessibility
- Adds accessibility attributes to links and images.
- Adds a note on using NVM for node.
2014-03-07 15:04:08 -08:00
Takashi Nakagawa 0812061274 chore(grunt): remove unnecessary white spaces 2014-03-07 15:03:57 -08:00
102 changed files with 4246 additions and 680 deletions
+2 -1
View File
@@ -1,5 +1,6 @@
{
"disallowKeywords": ["with"],
"disallowTrailingWhitespace": true,
"requireRightStickedOperators": ["!"]
"requireRightStickedOperators": ["!"],
"requireLeftStickedOperators": [","]
}
-1
View File
@@ -8,7 +8,6 @@
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
"disallowLeftStickedOperators": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
"disallowRightStickedOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
"requireLeftStickedOperators": [","],
"disallowImplicitTypeConversion": ["string"],
"disallowMultipleLineBreaks": true,
"disallowKeywordsOnNewLine": ["else"],
+68 -15
View File
@@ -1,3 +1,56 @@
<a name="v1.2.15"></a>
# v1.2.15 beer-underestimating (2014-03-21)
## Bug Fixes
- **$$RAFProvider:** check for webkitCancelRequestAnimationFrame
([e84da228](https://github.com/angular/angular.js/commit/e84da2283c4e195be557f7b06c8783fe502acbbb),
[#6526](https://github.com/angular/angular.js/issues/6526))
- **$$rAF:** always fallback to a $timeout incase native rAF isn't supported
([ee8e4a94](https://github.com/angular/angular.js/commit/ee8e4a946ed8f943e00846b88d8d51c0b2cd1fab),
[#6654](https://github.com/angular/angular.js/issues/6654))
- **$compile:** support templates with thead and tfoot root elements
([ca0ac649](https://github.com/angular/angular.js/commit/ca0ac649971ae4fb50419b38f92a98d2226eb696),
[#6289](https://github.com/angular/angular.js/issues/6289))
- **$http:**
- allow sending Blob data using $http
([fbb125a3](https://github.com/angular/angular.js/commit/fbb125a3af164e52af2f8119175b04cbbed2f331),
[#5012](https://github.com/angular/angular.js/issues/5012))
- don't covert 0 status codes to 404 for non-file protocols
([f108a2a9](https://github.com/angular/angular.js/commit/f108a2a994149ecc011e29f327bcb8e11adf72d9),
[#6074](https://github.com/angular/angular.js/issues/6074), [#6155](https://github.com/angular/angular.js/issues/6155))
- **$rootScope:**
- ng-repeat can't handle NaN values. #4605
([e48c28fe](https://github.com/angular/angular.js/commit/e48c28fe9292efe7af6205b2be116d2350990c73),
[#4605](https://github.com/angular/angular.js/issues/4605))
- $watchCollection should call listener with oldValue
([3dd95727](https://github.com/angular/angular.js/commit/3dd9572754c7bafec30dd625f5c611346959c969),
[#2621](https://github.com/angular/angular.js/issues/2621), [#5661](https://github.com/angular/angular.js/issues/5661), [#5688](https://github.com/angular/angular.js/issues/5688), [#6736](https://github.com/angular/angular.js/issues/6736))
- **angular.bootstrap:** only allow angular to load once
([0d60f8d3](https://github.com/angular/angular.js/commit/0d60f8d367e38224696749b0f7de04bd60649815),
[#5863](https://github.com/angular/angular.js/issues/5863), [#5587](https://github.com/angular/angular.js/issues/5587))
- **jqLite:** traverse `host` property for DocumentFragment in inheritedData()
([98d825e1](https://github.com/angular/angular.js/commit/98d825e10d3bf76f47e69abba857a8933c8cb7d9),
[#6637](https://github.com/angular/angular.js/issues/6637))
- **ngAnimate:** setting classNameFilter disables animation inside ng-if
([a41a2a1d](https://github.com/angular/angular.js/commit/a41a2a1d2ce20f86ac2709592e4ada527160e580),
[#6539](https://github.com/angular/angular.js/issues/6539))
- **ngCookie:** convert non-string values to string
([93d1c95c](https://github.com/angular/angular.js/commit/93d1c95c61dbfa565333bb64527a103242175af7),
[#6151](https://github.com/angular/angular.js/issues/6151), [#6220](https://github.com/angular/angular.js/issues/6220))
- **ngTouch:** update workaround for desktop Webkit quirk
([01a34f51](https://github.com/angular/angular.js/commit/01a34f513bb567ed6d4c81d00d7c2a777c0dae01),
[#6302](https://github.com/angular/angular.js/issues/6302))
- **orderBy:** support string predicates containing non-ident characters
([10d3e1e4](https://github.com/angular/angular.js/commit/10d3e1e4472ab9f5cf4418b6438ec2e0f2b0b288),
[#6143](https://github.com/angular/angular.js/issues/6143), [#6144](https://github.com/angular/angular.js/issues/6144))
- **select:** avoid checking option element selected properties in render
([dc149de9](https://github.com/angular/angular.js/commit/dc149de9364c66b988f169f67cad39577ba43434),
[#2448](https://github.com/angular/angular.js/issues/2448), [#5994](https://github.com/angular/angular.js/issues/5994), [#6769](https://github.com/angular/angular.js/issues/6769))
<a name="1.2.14"></a>
# 1.2.14 feisty-cryokinesis (2014-03-01)
@@ -275,26 +328,26 @@ The animation mock module has been renamed from `mock.animate` to `ngAnimateMock
## Breaking Changes
- **$http:** due to [e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
it is now necessary to separately specify default HTTP headers for PUT, POST and PATCH requests, as these no longer share a single object.
it is now necessary to separately specify default HTTP headers for PUT, POST and PATCH requests, as these no longer share a single object.
To migrate your code, follow the example below:
To migrate your code, follow the example below:
Before:
Before:
// Will apply to POST, PUT and PATCH methods
$httpProvider.defaults.headers.post = {
"X-MY-CSRF-HEADER": "..."
};
// Will apply to POST, PUT and PATCH methods
$httpProvider.defaults.headers.post = {
"X-MY-CSRF-HEADER": "..."
};
After:
After:
// POST, PUT and PATCH default headers must be specified separately,
// as they do not share data.
$httpProvider.defaults.headers.post =
$httpProvider.defaults.headers.put =
$httpProviders.defaults.headers.patch = {
"X-MY-CSRF-HEADER": "..."
};
// POST, PUT and PATCH default headers must be specified separately,
// as they do not share data.
$httpProvider.defaults.headers.post =
$httpProvider.defaults.headers.put =
$httpProviders.defaults.headers.patch = {
"X-MY-CSRF-HEADER": "..."
};
<a name="1.2.8"></a>
# 1.2.8 interdimensional-cartography (2014-01-10)
+3 -2
View File
@@ -1,5 +1,6 @@
var files = require('./angularFiles').files;
var util = require('./lib/grunt/utils.js');
var versionInfo = require('./lib/versions/version-info');
var path = require('path');
module.exports = function(grunt) {
@@ -8,10 +9,10 @@ module.exports = function(grunt) {
grunt.loadTasks('lib/grunt');
var NG_VERSION = util.getVersion();
var NG_VERSION = versionInfo.currentVersion;
NG_VERSION.cdn = versionInfo.currentPackage.cdnVersion;
var dist = 'angular-'+ NG_VERSION.full;
//global beforeEach
util.init();
+3 -3
View File
@@ -61,9 +61,9 @@ This process based on the idea of minimizing user pain
1. Label `origin: google` for issues from Google
1. Assign a milestone:
* Current 1.x.y milestone - regressions and urgent bugs only
* Backlog - fixes; changes that should go into a patch release
* Ice Box - new features; changes that belong inß a major/minor release
* Backlog - triaged fixes and features, should be the default choice
* Current 1.x.y milestone (e.g. 1.3.0-beta-2) - regressions and urgent bugs only
1. Unassign yourself from the issue
+11
View File
@@ -0,0 +1,11 @@
<h1>Oops!</h1>
<p>The page you requested does not exist. Perhaps you were looking for something else...</p>
<div ng-controller="Error404SearchCtrl">
<dl ng-repeat="(key, value) in results" ng-show="value.length" style="float: left; margin-right:20px">
<dt>{{ key }}</dt>
<dd ng-repeat="item in value"><a ng-href="{{ item.path }}">{{ item.name }}</a></dd>
</dl>
</div>
+19 -10
View File
@@ -184,10 +184,12 @@ h1,h2,h3,h4,h5,h6 {
}
pre {
padding:15px;
border:1px solid #ddd;
display:block;
border-radius:5px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
display: block;
white-space: pre-wrap;
word-break: normal;
}
.aside-nav a,
@@ -464,6 +466,10 @@ iframe.example {
background:rgb(189, 63, 66);
}
.type-hint-regexp {
background: rgb(90, 84, 189);
}
.runnable-example-frame {
width:100%;
height:300px;
@@ -501,10 +507,6 @@ h4 {
padding-top:20px;
}
.improve-docs {
float:right;
}
.btn {
color:#428bca;
position: relative;
@@ -538,10 +540,17 @@ h4 {
background:white!important;
}
.view-source, .improve-docs {
position:relative;
z-index:100;
}
.view-source {
margin-right:10px;
padding-right:10px;
border-right:1px solid #999;
}
.improve-docs {
float:right;
}
.return-arguments,
+3 -1
View File
@@ -2,6 +2,8 @@ angular.module('DocsController', [])
.controller('DocsController', function($scope, $rootScope, $location, $window, $cookies, NG_PAGES, NG_NAVIGATION, NG_VERSION) {
$scope.docsVersion = NG_VERSION.isSnapshot ? 'snapshot' : NG_VERSION.version;
$scope.fold = function(url) {
if(url) {
$scope.docs_fold = '/notes/' + url;
@@ -87,7 +89,7 @@ angular.module('DocsController', [])
breadcrumbPath += '/';
});
} else {
$scope.currentArea = null;
$scope.currentArea = NG_NAVIGATION['api'];
$scope.breadcrumb = [];
}
});
+4
View File
@@ -45,6 +45,10 @@ angular.module('search', [])
};
}])
.controller('Error404SearchCtrl', ['$scope', '$location', 'docsSearch', function($scope, $location, docsSearch) {
$scope.results = docsSearch($location.path().split(/[\/\.:]/).pop());
}])
.factory('lunrSearch', function() {
return function(properties) {
if (window.RUNNING_IN_NG_TEST_RUNNER) return null;
+4 -4
View File
@@ -21,10 +21,10 @@ angular.module('tutorials', [])
element.addClass('btn-group');
element.addClass('tutorial-nav');
element.append(templateMerge(
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="icon-step-backward"></i> Previous</li></a>\n' +
'<a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="icon-play"></i> Live Demo</li></a>\n' +
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="icon-search"></i> Code Diff</li></a>\n' +
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="icon-step-forward"></i></li></a>', props));
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-step-backward"></i> Previous</li></a>\n' +
'<a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="glyphicon glyphicon-play"></i> Live Demo</li></a>\n' +
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-search"></i> Code Diff</li></a>\n' +
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="glyphicon glyphicon-step-forward"></i></li></a>', props));
}
};
})
+4
View File
@@ -25,6 +25,10 @@ module.exports = function(config) {
require('./tag-defs/tutorial-step')
]);
config.append('processing.inlineTagDefinitions', [
require('./inline-tag-defs/type')
]);
config.set('processing.search.ignoreWordsFile', path.resolve(packagePath, 'ignore.words'));
config.prepend('rendering.templateFolders', [
+12
View File
@@ -0,0 +1,12 @@
var typeClassFilter = require('dgeni-packages/ngdoc/rendering/filters/type-class');
var encoder = new require('node-html-encoder').Encoder();
module.exports = {
name: 'type',
description: 'Replace with markup that displays a nice type',
handlerFactory: function() {
return function(doc, tagName, tagDescription) {
return '<a href="" class="' + typeClassFilter.process(tagDescription) + '">'+encoder.htmlEncode(tagDescription) + '</a>';
};
}
};
+6
View File
@@ -22,6 +22,12 @@ module.exports = {
_.forEach(docs, function(doc) {
if ( doc.docType === 'error' ) {
// Parse out the error info from the id
parts = doc.name.split(':');
doc.namespace = parts[0];
doc.name = parts[1];
var namespaceDoc = errorNamespaces[doc.namespace];
if ( !namespaceDoc ) {
// First time we came across this namespace, so create a new one
+4 -3
View File
@@ -1,4 +1,5 @@
var gruntUtils = require('../../../lib/grunt/utils');
var versionInfo = require('../../../lib/versions/version-info');
module.exports = {
name: 'git-data',
@@ -6,9 +7,9 @@ module.exports = {
description: 'This processor adds information from the local git repository to the extraData injectable',
init: function(config, injectables) {
injectables.value('gitData', {
version: gruntUtils.getVersion(),
versions: gruntUtils.getPreviousVersions(),
info: gruntUtils.getGitRepoInfo()
version: versionInfo.currentVersion,
versions: versionInfo.previousVersions,
info: versionInfo.gitRepoInfo
});
},
process: function(extraData, gitData) {
+8 -11
View File
@@ -11,17 +11,17 @@ var AREA_NAMES = {
};
function getNavGroup(pages, area, pageSorter, pageMapper) {
var navItems = _(pages)
// We don't want the child to include the index page as this is already catered for
.omit(function(page) { return page.id === 'index'; })
// Apply the supplied sorting function
.sortBy(pageSorter)
// Apply the supplied mapping function
.map(pageMapper)
.value();
return {
@@ -145,6 +145,9 @@ module.exports = {
_(docs)
.filter(function(doc) { return doc.area === 'api'; })
.filter(function(doc) { return doc.docType === 'module'; })
.forEach(function(doc) { if ( !doc.path ) {
log.warn('Missing path property for ', doc.id);
}})
.map(function(doc) { return _.pick(doc, ['id', 'module', 'docType', 'area']); })
.tap(function(docs) {
log.debug(docs);
@@ -173,7 +176,7 @@ module.exports = {
// - ngView
// - section "service"
// - $route
//
//
var areas = {};
_(navPages)
.groupBy('area')
@@ -188,12 +191,6 @@ module.exports = {
area.navGroups = navGroupMapper(pages, area);
});
_.forEach(docs, function(doc) {
if ( !doc.path ) {
log.warn('Missing path property for ', doc.id);
}
});
// Extract a list of basic page information for mapping paths to paritals and for client side searching
var pages = _(docs)
.map(function(doc) {
@@ -175,7 +175,7 @@
<div class="container main-grid main-header-grid">
<div class="grid-left">
<div ng-controller="DocsVersionsCtrl" class="picker version-picker">
<select ng-options="v as ('v' + v.full) group by (v.isStable?'Stable':'Unstable') for v in docs_versions"
<select ng-options="v as ('v' + v.version + (v.isSnapshot ? ' (snapshot)' : '')) group by (v.isStable?'Stable':'Unstable') for v in docs_versions"
ng-model="docs_version"
ng-change="jumpToDocsVersion(docs_version)"
class="docs-version-jump">
@@ -219,7 +219,7 @@
</div>
<div class="grid-right">
<div id="loading" ng-show="loading">Loading...</div>
<div ng-hide="loading" ng-include="currentPage.outputPath" onload="afterPartialLoaded()" autoscroll></div>
<div ng-hide="loading" ng-include="currentPage.outputPath || 'Error404.html'" onload="afterPartialLoaded()" autoscroll></div>
</div>
</div>
</section>
@@ -0,0 +1,27 @@
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
is HTML and wrap each line in a <p> - thus breaking the HTML #}
<div>
<a ng-href="http://plnkr.co/edit/ngdoc:{$ doc.example.id $}@{{docsVersion}}?p=preview" class="btn pull-right" target="_blank">
<i class="glyphicon glyphicon-edit">&nbsp;</i>
Edit in Plunker</a>
<div class="runnable-example"
path="{$ doc.example.outputFolder $}"
{%- for attrName, attrValue in doc.example.attributes %}
{$ attrName $}="{$ attrValue $}"{% endfor %}>
{% for fileName, file in doc.example.files %}
<div class="runnable-example-file" {% for attrName, attrValue in file.attributes %}
{$ attrName $}="{$ attrValue $}"{% endfor %}>
{% code -%}
{$ file.fileContents $}
{%- endcode %}
</div>
{% endfor %}
<iframe class="runnable-example-frame" src="{$ doc.example.outputFolder $}/index.html" name="{$ doc.example.id $}"></iframe>
</div>
</div>
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
above is HTML and wrap each line in a <p> - thus breaking the HTML #}
@@ -30,6 +30,9 @@ Following are invalid uses of this directive:
<!-- ERROR because `myFn()=localValue` is an invalid statement -->
<my-directive bind="myFn()">
<!-- ERROR because attribute bind wasn't provided -->
<my-directive>
```
+2 -2
View File
@@ -38,12 +38,12 @@ You can also get this error if you accidentally load AngularJS itself more than
<html ng-app>
<head>
<script src="angular.js"></script>
...
</head>
<body>
...
<script src="angular.js"></script>
+1 -1
View File
@@ -325,7 +325,7 @@ This will not render properly, unless we do some scope magic.
The first issue we have to solve is that the dialog box template expects `title` to be defined.
But we would like the template's scope property `title` to be the result of interpolating the
`<dialog>` element's `title` attribute (i.e. `"Hello {{username}}"`. Furthermore, the buttons expect
`<dialog>` element's `title` attribute (i.e. `"Hello {{username}}"`). Furthermore, the buttons expect
the `onOk` and `onCancel` functions to be present in the scope. This limits the usefulness of the
widget. To solve the mapping issue we use the `locals` to create local variables which the template
expects as follows:
+8 -8
View File
@@ -32,9 +32,9 @@ In the following example we will build a form to calculate the costs of an invoi
Let's start with input fields for quantity and cost whose values are multiplied to produce the total of the invoice:
<example>
<example name="guide-concepts-1" ng-app-included="true">
<file name="index.html">
<div ng-init="qty=1;cost=2">
<div ng-app ng-init="qty=1;cost=2">
<b>Invoice:</b>
<div>
Quantity: <input type="number" ng-model="qty" required >
@@ -102,7 +102,7 @@ The concept behind this is <a name="databinding">"{@link databinding two-way dat
Let's add some more logic to the example that allows us to enter and calculate the costs in
different currencies and also pay the invoice.
<example module="invoice1">
<example name="guide-concepts-2" ng-app-included="true" >
<file name="invoice1.js">
angular.module('invoice1', [])
.controller('InvoiceController', function() {
@@ -128,7 +128,7 @@ different currencies and also pay the invoice.
});
</file>
<file name="index.html">
<div ng-controller="InvoiceController as invoice">
<div ng-app="invoice1" ng-controller="InvoiceController as invoice">
<b>Invoice:</b>
<div>
Quantity: <input type="number" ng-model="invoice.qty" required >
@@ -191,7 +191,7 @@ from the web, e.g. by calling the Yahoo Finance API, without changing the contro
Let's refactor our example and move the currency conversion into a service in another file:
<example module="invoice2">
<example name="guide-concepts-2" ng-app-included="true">
<file name="finance2.js">
angular.module('finance2', [])
.factory('currencyConverter', function() {
@@ -228,7 +228,7 @@ Let's refactor our example and move the currency conversion into a service in an
}]);
</file>
<file name="index.html">
<div ng-controller="InvoiceController as invoice">
<div ng-app="invoice2" ng-controller="InvoiceController as invoice">
<b>Invoice:</b>
<div>
Quantity: <input type="number" ng-model="invoice.qty" required >
@@ -302,7 +302,7 @@ to something shorter like `a`.
Let's finish our example by fetching the exchange rates from the Yahoo Finance API.
The following example shows how this is done with Angular:
<example module="invoice3">
<example name="guide-concepts-3" ng-app-included="true">
<file name="invoice3.js">
angular.module('invoice3', ['finance3'])
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
@@ -356,7 +356,7 @@ The following example shows how this is done with Angular:
}]);
</file>
<file name="index.html">
<div ng-controller="InvoiceController as invoice">
<div ng-app="invoice3" ng-controller="InvoiceController as invoice">
<b>Invoice:</b>
<div>
Quantity: <input type="number" ng-model="invoice.qty" required >
+2 -2
View File
@@ -293,9 +293,9 @@ The `restrict` option is typically set to:
* `'E'` - only matches element name
* `'C'` - only matches class name
These restictions can all be combined as needed:
These restrictions can all be combined as needed:
* `'AEC'` - matches either attribure or element or class name
* `'AEC'` - matches either attribute or element or class name
Let's change our directive to use `restrict: 'E'`:
+18 -20
View File
@@ -56,7 +56,7 @@ Note that `novalidate` is used to disable browser's native form validation.
# Using CSS classes
To allow styling of form as well as controls, `ngModel` add these CSS classes:
To allow styling of form as well as controls, `ngModel` adds these CSS classes:
- `ng-valid`
- `ng-invalid`
@@ -211,26 +211,24 @@ In the following example we create two directives.
<example module="form-example1">
<file name="index.html">
<div ng-controller="Controller">
<form name="form" class="css-form" novalidate>
<div>
Size (integer 0 - 10):
<input type="number" ng-model="size" name="size"
min="0" max="10" integer />{{size}}<br />
<span ng-show="form.size.$error.integer">This is not valid integer!</span>
<span ng-show="form.size.$error.min || form.size.$error.max">
The value must be in range 0 to 10!</span>
</div>
<form name="form" class="css-form" novalidate>
<div>
Size (integer 0 - 10):
<input type="number" ng-model="size" name="size"
min="0" max="10" integer />{{size}}<br />
<span ng-show="form.size.$error.integer">This is not valid integer!</span>
<span ng-show="form.size.$error.min || form.size.$error.max">
The value must be in range 0 to 10!</span>
</div>
<div>
Length (float):
<input type="text" ng-model="length" name="length" smart-float />
{{length}}<br />
<span ng-show="form.length.$error.float">
This is not a valid float number!</span>
</div>
</form>
</div>
<div>
Length (float):
<input type="text" ng-model="length" name="length" smart-float />
{{length}}<br />
<span ng-show="form.length.$error.float">
This is not a valid float number!</span>
</div>
</form>
</file>
<file name="script.js">
+6 -6
View File
@@ -26,7 +26,7 @@ To make your Angular application work on IE please make sure that:
1. You polyfill JSON.stringify for IE7 and below. You can use
[JSON2](https://github.com/douglascrockford/JSON-js) or
[JSON3](http://bestiejs.github.com/json3/) polyfills for this.
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org">
@@ -42,7 +42,7 @@ To make your Angular application work on IE please make sure that:
```
2. add `id="ng-app"` to the root element in conjunction with `ng-app` attribute
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
@@ -54,7 +54,7 @@ To make your Angular application work on IE please make sure that:
`<div ng-view>` instead), or
4. if you **do use** custom element tags, then you must take these steps to make IE 8 and below happy:
```html
<!doctype html>
<html xmlns:ng="http://angularjs.org" id="ng-app" ng-app="optionalModuleName">
@@ -64,7 +64,7 @@ To make your Angular application work on IE please make sure that:
document.createElement('ng-include');
document.createElement('ng-pluralize');
document.createElement('ng-view');
// Optionally these for CSS
document.createElement('ng:include');
document.createElement('ng:pluralize');
@@ -79,7 +79,7 @@ To make your Angular application work on IE please make sure that:
```
5. Use `ng-style` tags instead of `style="{{ someCss }}"`. The later works in Chrome and Firefox
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
The **important** parts are:
@@ -165,7 +165,7 @@ In IE, the behavior is that the `BODY` element has three children:
## CSS Styling of Custom Tag Names
To make CSS selectors work with custom elements, the custom element name must be pre-created with
To make CSS selectors work with custom elements, the custom element name must be pre-created with
`document.createElement('my-tag')` regardless of XML namespace.
```html
+42 -5
View File
@@ -7,7 +7,7 @@ AngularJS version 1.2 introduces several breaking changes that may require chang
application's source code.
Although we try to avoid breaking changes, there are some cases where it is unavoidable.
AngularJS 1.2 has undergone a thourough security review to make applications safer by default,
AngularJS 1.2 has undergone a thorough security review to make applications safer by default,
which has driven many of these changes. Several new features, especially animations, would not
be possible without a few changes. Finally, some outstanding bugs were best fixed by changing
an existing API.
@@ -43,11 +43,12 @@ below should still apply, but you may want to consult the
<li>{@link guide/migration#ngscenario ngScenario}</li>
<li>{@link guide/migration#nginclude-and-ngview-replace-its-entire-element-on-update ngInclude and ngView replace its entire element on update}</li>
<li>{@link guide/migration#urls-are-now-sanitized-against-a-whitelist URLs are now sanitized against a whitelist}</li>
<li>{@link guide/migration#isolate-scope-only-exposed-to-directives-with-property Isolate scope only exposed to directives with <code>scope</code> property}</li>
<li>{@link guide/migration#isolate-scope-only-exposed-to-directives-with-scope-property Isolate scope only exposed to directives with <code>scope</code> property}</li>
<li>{@link guide/migration#change-to-interpolation-priority Change to interpolation priority}</li>
<li>{@link guide/migration#underscore-prefixed/suffixed-properties-are-non-bindable Underscore-prefixed/suffixed properties are non-bindable}</li>
<li>{@link guide/migration#you-cannot-bind-to-select[multiple] You cannot bind to select[multiple]}</li>
<li>{@link guide/migration#uncommon-region-specific-local-files-were-removed-from-i18n Uncommon region-specific local files were removed from i18n}</li>
<li>{@link guide/migration#services-can-now-return-functions Services can now return functions}</li>
</ul>
@@ -493,7 +494,7 @@ See [31f190d4](https://github.com/angular/angular.js/commit/31f190d4d53921d32253
the priority of ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView has changed. This could affect directives that explicitly specify their priority.
In order to make ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView work together in all common scenarios their directives are being adjusted to achieve the following precendence:
In order to make ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView work together in all common scenarios their directives are being adjusted to achieve the following precedence:
Directive | Old Priority | New Priority
@@ -532,7 +533,7 @@ See [7d69d52a](https://github.com/angular/angular.js/commit/7d69d52acff8578e0f7d
A whitelist configured via `$compileProvider` can be used to configure what URLs are considered safe.
By default all common protocol prefixes are whitelisted including `data:` URIs with mime types `image/*`.
This change sholdn't impact apps that don't contain malicious image links.
This change shouldn't impact apps that don't contain malicious image links.
See [1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d61286840177607edd552a9df97),
[3e39ac7e](https://github.com/angular/angular.js/commit/3e39ac7e1b10d4812a44dad2f959a93361cd823b).
@@ -613,7 +614,7 @@ controller.) That's easier said that done for two reasons:
someone on the scope chain for JavaScript use, you also expose it to
Angular expressions
2. The new `controller as` syntax that's now in increased usage exposes the
entire controller on the scope chain greatly increaing the exposed surface.
entire controller on the scope chain greatly increasing the exposed surface.
Though Angular expressions are written and controlled by the developer, they:
@@ -653,3 +654,39 @@ load and use your copy of the locale file provided that you maintain it yourself
See [6382e21f](https://github.com/angular/angular.js/commit/6382e21fb28541a2484ac1a241d41cf9fbbe9d2c).
## Services can now return functions
Previously, the service constructor only returned objects regardless of whether a function was returned.
Now, `$injector.instantiate` (and thus `$provide.service`) behaves the same as the native
`new` operator and allows functions to be returned as a service.
If using a JavaScript preprocessor it's quite possible when upgrading that services could start behaving incorrectly.
Make sure your services return the correct type wanted.
**Coffeescript example**
```
myApp.service 'applicationSrvc', ->
@something = "value"
@someFunct = ->
"something else"
```
pre 1.2 this service would return the whole object as the service.
post 1.2 this service returns `someFunct` as the value of the service
you would need to change this services to
```
myApp.service 'applicationSrvc', ->
@something = "value"
@someFunct = ->
"something else"
return
```
to continue to return the complete instance.
See [c22adbf1](https://github.com/angular/angular.js/commit/c22adbf160f32c1839fbb35382b7a8c6bcec2927).
+4 -3
View File
@@ -66,8 +66,9 @@ that you break your application to multiple modules like this:
* And an application level module which depends on the above modules and contains any
initialization code.
We've also written a document on how we organize large apps at Google and on how to write
reusable components.
We've also
[written a document](http://blog.angularjs.org/2014/02/an-angularjs-style-guide-and-best.html)
on how we organize large apps at Google.
The above is a suggestion. Tailor it to your needs.
@@ -172,7 +173,7 @@ angular.module('myModule', []).
<div class="alert alert-info">
When bootstrapping, first Angular applies all constant definitions.
Then Angular applies configuration blocks in the order same order they were registered.
Then Angular applies configuration blocks in the same order they were registered.
</div>
## Run Blocks
+1 -1
View File
@@ -143,7 +143,7 @@ primitive, object literal, function, or even an instance of a custom type.
## Service Recipe
JavaScript developers often use custom types to write object-oriented code. Let's explore how we
could launch a unicorn into the space via our `unicornLauncher` service that is an instance of
could launch a unicorn into space via our `unicornLauncher` service that is an instance of
custom type:
```javascript
+7 -7
View File
@@ -11,15 +11,15 @@ Angular services are:
* Lazily instantiated Angular only instantiates a service when an application component depends
on it.
* Singletons Each component is dependent on a service gets a reference to the single instance
* Singletons Each component dependent on a service gets a reference to the single instance
generated by the service factory.
Angular offers several useful services (like {@link ng.$http `$http`}) but for most applications
Angular offers several useful services (like {@link ng.$http `$http`}), but for most applications
you'll also want to {@link services#creating-services create your own}.
<div class="alert alert-info">
**Note:** Like other core Angular identifiers built-in services always start with `$`
(i.e. `$http`).
(e.g. `$http`).
</div>
@@ -129,7 +129,7 @@ injection of `$window`, `$scope`, and our `notify` service:
</file>
</example>
<div class="alert alert-error">
<div class="alert alert-danger">
**Careful:** If you plan to [minify](http://en.wikipedia.org/wiki/Minification_(programming) your code,
your variable names will get renamed unless you use one of the annotation techniques above.
</div>
@@ -215,8 +215,8 @@ In the example, note that:
{@link ng.$log `$log`} services.
* The `routeTemplateMonitor` service depends on the built-in {@link ngRoute.$route `$route`}
service and our custom `batchLog` service.
* Both services use the and array notation to declare their dependencies.
* That the order of identifiers in the array is the same as the order of argument
* Both services use the array notation to declare their dependencies.
* The order of identifiers in the array is the same as the order of argument
names in the factory function.
### Registering a Service with `$provide`
@@ -234,7 +234,7 @@ angular.module('myModule', []).config(function($provide) {
});
```
This is technique is often used in unit tests to mock out a service's dependencies.
This technique is often used in unit tests to mock out a service's dependencies.
## Unit Testing
+1 -1
View File
@@ -258,7 +258,7 @@ expect($scope.strength).toEqual('weak');
```
Notice that the test is not only much shorter, it is also easier to follow what is happening. We say
that such a test tells a story, rather then asserting random bits which don't seem to be related.
that such a test tells a story, rather than asserting random bits which don't seem to be related.
## Filters
{@link ng.$filterProvider Filters} are functions which transform the data into a user readable
+6 -6
View File
@@ -11,12 +11,12 @@ See the [contributing guidelines](https://github.com/angular/angular.js/blob/mas
for how to contribute your own code to AngularJS.
1. {@link #building-and-testing-angularjs_installing-dependencies Installing Dependencies}
2. {@link #building-and-testing-angularjs_forking-angular-on-github Forking Angular on Github}
3. {@link #building-and-testing-angularjs_building-angularjs Building AngularJS}
4. {@link #building-and-testing-angularjs_running-a-local-development-web-server Running a Local Development Web Server}
5. {@link #building-and-testing-angularjs_running-the-unit-test-suite Running the Unit Test Suite}
6. {@link #building-and-testing-angularjs_running-the-end-to-end-test-suite Running the End-to-end Test Suite}
1. {@link misc/contribute#installing-dependencies Installing Dependencies}
2. {@link misc/contribute#forking-angular-on-github Forking Angular on Github}
3. {@link misc/contribute#building-angularjs Building AngularJS}
4. {@link misc/contribute#running-a-local-development-web-server Running a Local Development Web Server}
5. {@link misc/contribute#running-the-unit-test-suite Running the Unit Test Suite}
6. {@link misc/contribute#running-the-end-to-end-test-suite Running the End-to-end Test Suite}
## Installing Dependencies
+36 -27
View File
@@ -9,7 +9,8 @@ the construction of an AngularJS web app. The app you will build is a catalog th
of Android devices, lets you filter the list to see only devices that interest you, and then view
details for any device.
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413">
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413" alt="demo
application running in the browser">
Work through the tutorial to see how Angular makes browsers smarter — without the use of extensions
or plug-ins. As you work through the tutorial, you will:
@@ -57,63 +58,71 @@ and follow the instructions for setting up your computer.
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
<ol>
<li><p>You'll need Git, which you can get from
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
<a href="http://git-scm.com/download" title="Git site download">the Git site</a>.</p></li>
<li><p>Clone the angular-phonecat repository located at
<a href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
<a href="https://github.com/angular/angular-phonecat" title="Github Phonecat Repo">Github</a> by
running the following command:</p>
<pre>git clone https://github.com/angular/angular-phonecat.git</pre>
<p>This command creates the <code>angular-phonecat</code> directory in your current
directory.</p></li>
<p>This command creates the <code>angular-phonecat</code> directory in your current directory.</p></li>
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
<pre>cd angular-phonecat</pre>
<p>The tutorial instructions, from now on, assume you are running all commands from the <code>angular-phonecat</code>
directory.</p></li>
<p>The tutorial instructions, from now on, assume you are running all commands from the
<code>angular-phonecat</code> directory.</p></li>
<li><p>You will also need Node.js and Karma to run unit tests, so please verify that you have
<a href="http://nodejs.org/">Node.js</a> v0.10 or better installed
<a href="http://nodejs.org/" title="NodeJS org">Node.js</a> v0.10 or better installed
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
command in a terminal window:</p></li>
<pre>node --version</pre>
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> and its plugins if you
don't have it already:</p>
<div class="alert alert-info">**Helpful note:** If you need to run a different version of
node.js in your local environment, consider installing
<a href="https://github.com/creationix/nvm" title="Node Version Manager Github Repo link">
Node Version Manager (nvm)</a>.</div>
<p>Additionally install <a href="http://karma-runner.github.io/" title="Karma site">Karma</a> and
its plugins if you don't have it already:</p>
<pre>
npm install
</pre></li>
<li><p>You will need an http server running on your system. Mac and Linux machines typically
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
to run a simple bundled http server: <code>node scripts/web-server.js</code>.</p></li>
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
</ol>
</div>
<div class="tab-pane well" id="git-win" title="Git on Windows">
<ol>
<li><p>You will need Node.js and Karma to run unit tests, so please verify that you have
<a href="http://nodejs.org/">Node.js</a> v0.10 or better installed
<a href="http://nodejs.org/" title="NodeJS site">Node.js</a> v0.10 or better installed
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
command in a terminal window:</p>
<pre>node --version</pre>
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> if you
don't have it already:</p>
<div class="alert alert-info">**Helpful note:** If you need to run a different version of
node.js in your local environment, consider installing
<a href="https://github.com/creationix/nvm" title="Node Version Manager Github Repo link">
Node Version Manager (nvm)</a>.</div>
<p>Additionally install <a href="http://karma-runner.github.io/" title="Karma site">Karma</a>
if you don't have it already:</p>
<pre>npm install -g karma</pre>
</li>
<li><p>You'll also need Git, which you can get from
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
<a href="http://git-scm.com/download" title="Git site download">the Git site</a>.</p></li>
<li><p>Clone the angular-phonecat repository located at <a
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
<pre>git clone https://github.com/angular/angular-phonecat.git</pre>
href="https://github.com/angular/angular-phonecat" "Github Angular-phonecat Repo">Github</a> by running
the following command:</p><pre>git clone https://github.com/angular/angular-phonecat.git</pre>
<p>This command creates the <code>angular-phonecat</code> directory in your current directory.</p></li>
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
<pre>cd angular-phonecat</pre>
<p>The tutorial instructions assume you are running all commands from the <code>angular-phonecat</code>
directory.</p>
directory.</p>
<p>You should run all <code>git</code> commands from Git bash.</p>
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
executed from the Windows command line.</li>
<li><p>You need an http server running on your system, but if you don't already have one
already installed, you can use <code>node</code> to run a simple
bundled http server: <code>node scripts\web-server.js</code>.</p></li>
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be executed from the
Windows command line.</li>
<li><p>You need an http server running on your system, but if you don't already have one already
installed, you can use <code>node</code> to run <code>scripts\web-server.js</code>, a simple bundled
http server.</p></li>
</ol>
</div>
The last thing to do is to make sure your computer has a web browser and a good text editor
installed. Now, let's get some cool stuff done!
The last thing to do is to make sure your computer has a web browser and a good text editor installed. Now,
let's get some cool stuff done!
{@link step_00 <span class="btn btn-primary">Get Started!</span>}
<a href="tutorial/step_00" title="Next Step"><span class="btn btn-primary">Get Started!</span></a>
+5 -1
View File
@@ -183,7 +183,7 @@ is available to be injected.
### Writing and Running Tests
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
writing tests. Although Angular does not require you to use Jasmine, we wrote all of the tests in
this tutorial in Jasmine. You can learn about Jasmine on the [Jasmine home page](http://pivotal.github.com/jasmine/) and at the [Jasmine docs](http://pivotal.github.io/jasmine/).
this tutorial in Jasmine. You can learn about Jasmine on the [Jasmine home page](http://jasmine.github.io/) and at the [Jasmine docs](http://jasmine.github.io/).
The angular-seed project is pre-configured to run all unit tests using [Karma](http://karma-runner.github.io/). Ensure that the necessary karma plugins are installed.
You can do this by issuing `npm install` into your terminal.
@@ -226,6 +226,10 @@ To run the test, do the following:
Refresh your browser and verify that it says "Hello, World!".
* Update the unit test for the controler in ./tests/unit/controlersSpec.js to reflect the previous change. For example by adding:
expect(scope.name).toBe('World');
* Create a repeater that constructs a simple table:
<table>
+2 -2
View File
@@ -91,7 +91,7 @@ phonecatApp.controller('PhoneListCtrl', function ($scope) {
record. This property is used to order phones by age.
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
not set a default value here, the `orderBy` filter would remain uninitialized until our
not set a default value here, the `orderBy` filter would remain uninitialized until our
user picked an option from the drop down menu.
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
@@ -117,7 +117,7 @@ describe('PhoneCat controllers', function() {
var scope, ctrl;
beforeEach(module('phonecatApp'));
beforeEach(inject(function($controller) {
scope = {};
ctrl = $controller('PhoneListCtrl', {$scope:scope});
+7 -8
View File
@@ -7,7 +7,7 @@
Enough of building an app with three phones in a hard-coded dataset! Let's fetch a larger dataset
from our server using one of Angular's built-in {@link guide/dev_guide.services services} called {@link
from our server using one of Angular's built-in {@link guide/services services} called {@link
ng.$http $http}. We will use Angular's {@link guide/di dependency
injection (DI)} to provide the service to the `PhoneListCtrl` controller.
@@ -20,7 +20,6 @@ You should now see a list of 20 phones.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-4...step-5):
## Data
The `app/phones/phones.json` file in your project is a dataset that contains a larger list of phones
stored in the JSON format.
@@ -44,7 +43,7 @@ Following is a sample of the file:
We'll use Angular's {@link ng.$http $http} service in our controller to make an HTTP
request to your web server to fetch the data in the `app/phones/phones.json` file. `$http` is just
one of several built-in {@link guide/dev_guide.services angular services} that handle common operations
one of several built-in {@link guide/services Angular services} that handle common operations
in web apps. Angular injects these services for you where you need them.
Services are managed by Angular's {@link guide/di DI subsystem}. Dependency injection
@@ -74,10 +73,10 @@ tutorial.)
The `$http` service returns a {@link ng.$q promise object} with a `success`
method. We call this method to handle the asynchronous response and assign the phone data to the
scope controlled by this controller, as a model called `phones`. Notice that angular detected the
scope controlled by this controller, as a model called `phones`. Notice that Angular detected the
json response and parsed it for us!
To use a service in angular, you simply declare the names of the dependencies you need as arguments
To use a service in Angular, you simply declare the names of the dependencies you need as arguments
to the controller's constructor function, as follows:
phonecatApp.controller('PhoneListCtrl', function ($scope, $http) {...}
@@ -96,7 +95,7 @@ dependencies.
### `$` Prefix Naming Convention
You can create your own services, and in fact we will do exactly that in step 11. As a naming
convention, angular's built-in services, Scope methods and a few other Angular APIs have a `$`
convention, Angular's built-in services, Scope methods and a few other Angular APIs have a `$`
prefix in front of the name.
The `$` prefix is there to namespace Angular-provided services.
@@ -167,7 +166,7 @@ __`test/unit/controllersSpec.js`:__
Because we started using dependency injection and our controller has dependencies, constructing the
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, the recommended (and easier) way
is to create a controller in the test environment in the same way that angular does it in the
is to create a controller in the test environment in the same way that Angular does it in the
production code behind the scenes, as follows:
```js
@@ -269,7 +268,7 @@ to the first 5 in the list. Use the following code in the `$http` callback:
# Summary
Now that you have learned how easy it is to use angular services (thanks to Angular's dependency
Now that you have learned how easy it is to use Angular services (thanks to Angular's dependency
injection), go to {@link step_06 step 6}, where you will add some
thumbnail images of phones and some links.
+1 -1
View File
@@ -63,7 +63,7 @@ the element attribute.
We also added phone images next to each record using an image tag with the {@link
ng.directive:ngSrc ngSrc} directive. That directive prevents the
browser from treating the angular `{{ expression }}` markup literally, and initiating a request to
browser from treating the Angular `{{ expression }}` markup literally, and initiating a request to
invalid url `http://localhost:8000/app/{{phone.imageUrl}}`, which it would have done if we had only
specified an attribute binding in a regular `src` attribute (`<img src="{{phone.imageUrl}}">`).
Using the `ngSrc` directive prevents the browser from making an http request to an invalid location.
+1 -1
View File
@@ -114,7 +114,7 @@ Our application routes are defined as follows:
view, Angular will use the `phone-list.html` template and the `PhoneListCtrl` controller.
* The phone details view will be shown when the URL hash fragment matches '/phone/:phoneId', where
`:phoneId` is a variable part of the URL. To construct the phone details view, angular will use the
`:phoneId` is a variable part of the URL. To construct the phone details view, Angular will use the
`phone-detail.html` template and the `PhoneDetailCtrl` controller.
We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new,
+2 -2
View File
@@ -79,7 +79,7 @@ route by the `$route` service.
## Template
The TBD placeholder line has been replaced with lists and bindings that comprise the phone details.
Note where we use the angular `{{expression}}` markup and `ngRepeat` to project phone data from
Note where we use the Angular `{{expression}}` markup and `ngRepeat` to project phone data from
our model into the view.
@@ -107,7 +107,7 @@ __`app/partials/phone-detail.html`:__
</dl>
</li>
...
</li>
<li>
<span>Additional Features</span>
<dd>{{phone.additionalFeatures}}</dd>
</li>
+2 -2
View File
@@ -40,13 +40,13 @@ The name of our filter is "checkmark". The `input` evaluates to either `true` or
return one of the two unicode characters we have chosen to represent true (`\u2713` -> ✓) or false (`\u2718` -> ✘).
Now that our filter is ready, we need to register the `phonecatFilters` module as a dependency for
our main `phonecat` module.
our main `phonecatApp` module.
__`app/js/app.js`:__
```js
...
angular.module('phonecatApp', ['phonecatFilters']).
angular.module('phonecatApp', ['ngRoute','phonecatControllers','phonecatFilters']).
...
```
+2 -2
View File
@@ -22,8 +22,8 @@ The most important changes are listed below. You can see the full diff on [GitHu
## Template
The custom service is defined in `app/js/services.js` so we need to include this file in our layout
template. Additionally, we also need to load the `angular-resource.js` file, which contains the
{@link api/ngResource ngResource} module and in it the {@link api/ngResource.$resource $resource}
template. Additionally, we also need to load the `angular-resource.js` file, which contains the
{@link api/ngResource ngResource} module and in it the {@link api/ngResource.$resource $resource}
service, that we'll soon use:
__`app/index.html`.__
+8 -6
View File
@@ -20,7 +20,8 @@ a dependency with the application module, will enable animations throughout the
Common `ng` directives automatically trigger hooks for animations to tap into. When an animation is found
then the animation will run in between the standard DOM operation that is being issued on the element at
the given time (e.g. inserting and removing nodes on ngRepeat or adding and removing classes on ngClass).
the given time (e.g. inserting and removing nodes on {@link api/ng.directive:ngRepeat `ngRepeat`} or adding
and removing classes on {@link api/ng.directive:ngClass `ngClass`}).
The most important changes are listed below. You can see the full diff on
[GitHub](https://github.com/angular/angular-phonecat/compare/step-11...step-12):
@@ -34,9 +35,10 @@ To get an idea of how animations work with AngularJS, please read the
## Template
The changes required within the HTML template code is to link the asset files which define the animations as well
as the `angular-animate.js` file. The animation module, known as `ngAnimate`, is defined within
`angular-animate.js` and contains the code necessary to make your application become animation aware.
The changes required within the HTML template code is to link the asset files which define the animations as
well as the `angular-animate.js` file. The animation module, known as {@link api/ngAnimate `ngAnimate`}, is
defined within `angular-animate.js` and contains the code necessary to make your application become animation
aware.
Here's what needs to changed in the index file:
@@ -83,7 +85,7 @@ __`app/js/app.js`.__
```js
// ...
angular.module('phonecat', [
angular.module('phonecatApp', [
'ngRoute',
'phonecatAnimations',
@@ -197,7 +199,7 @@ which are described in detail below.
## Animating `ngView` with CSS Keyframe Animations
Next let's add an animation for transitions between route changes in `ngView`.
Next let's add an animation for transitions between route changes in {@link api/ngRoute.directive:ngView `ngView`}.
To start, let's add a new CSS class to our HTML like we did in the example above.
This time, instead of the `ng-repeat` element, let's add it to the element containing the ng-view directive.
+9 -7
View File
@@ -1,13 +1,12 @@
var path = require('canonical-path');
var gruntUtils = require('../lib/grunt/utils');
var versionInfo = require('../lib/versions/version-info');
var basePath = __dirname;
var basePackage = require('./config');
module.exports = function(config) {
var version = gruntUtils.getVersion();
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + version.cdn;
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + versionInfo.currentPackage.cdnVersion;
var getVersion = function(component, sourceFolder, packageFile) {
sourceFolder = sourceFolder || '../bower_components';
@@ -25,9 +24,12 @@ module.exports = function(config) {
{ pattern: '**/*.ngdoc', basePath: path.resolve(basePath, 'content') }
]);
config.set('processing.stopOnError', true);
config.set('processing.errors.minerrInfoPath', path.resolve(basePath, '../build/errors.json'));
config.set('rendering.outputFolder', '../build/docs');
config.set('rendering.contentsFolder', 'partials');
config.set('logging.level', 'info');
@@ -38,7 +40,7 @@ module.exports = function(config) {
commonFiles: {
scripts: [ '../../../angular.js' ]
},
dependencyPath: '../../..'
dependencyPath: '../../../'
},
scripts: [
'../angular.js',
@@ -73,7 +75,7 @@ module.exports = function(config) {
commonFiles: {
scripts: [ '../../../angular.min.js' ]
},
dependencyPath: '../../..'
dependencyPath: '../../../'
},
scripts: [
'../angular.min.js',
@@ -111,7 +113,7 @@ module.exports = function(config) {
'../../../angular.js'
]
},
dependencyPath: '../../..'
dependencyPath: '../../../'
},
scripts: [
'components/jquery-' + getVersion('jquery') + '/jquery.js',
@@ -147,7 +149,7 @@ module.exports = function(config) {
commonFiles: {
scripts: [ cdnUrl + '/angular.min.js' ]
},
dependencyPath: cdnUrl
dependencyPath: cdnUrl + '/'
},
scripts: [
cdnUrl + '/angular.min.js',
+5 -1
View File
@@ -49,7 +49,11 @@ gulp.task('assets', ['bower'], function() {
gulp.task('doc-gen', function() {
return docGenerator('docs.config.js').generateDocs();
return docGenerator('docs.config.js')
.generateDocs()
.catch(function(error) {
process.exit(1);
});
});
// JSHint the example and protractor test files
+1 -1
View File
@@ -33,7 +33,7 @@ describe("convertDatetimeData", function() {
AMPMS: ['AM', 'PM'],
DATEFORMATS: ['a', 'b', 'c', 'd'],
TIMEFORMATS: ['e', 'f', 'g', 'h'] };
it('should convert empty datetime obj', function() {
var processedData = convert(dataObj);
expect(processedData.MONTH).toEqual(['Enero', 'Pebrero']);
+1 -1
View File
@@ -10,7 +10,7 @@ set -xe
# Define reasonable set of browsers in case we are running manually from commandline
if [[ -z "$BROWSERS" ]]
then
BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh,/Users/jenkins/bin/ie8.sh,/Users/jenkins/bin/ie9.sh"
BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh"
fi
# CLEAN #
+6 -176
View File
@@ -4,8 +4,9 @@ var shell = require('shelljs');
var grunt = require('grunt');
var spawn = require('child_process').spawn;
var semver = require('semver');
var _ = require('lodash');
var version, pkg;
var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n';
var PORT_MIN = 8000;
@@ -23,23 +24,6 @@ var getRandomPorts = function() {
];
};
var getPackage = function() {
if ( !pkg ) {
// Search up the folder hierarchy for the first package.json
var packageFolder = path.resolve('.');
while ( !fs.existsSync(path.join(packageFolder, 'package.json')) ) {
var parent = path.dirname(packageFolder);
if ( parent === packageFolder) { break; }
packageFolder = parent;
}
pkg = JSON.parse(fs.readFileSync(path.join(packageFolder,'package.json'), 'UTF-8'));
}
return pkg;
};
module.exports = {
@@ -50,160 +34,6 @@ module.exports = {
},
getGitRepoInfo: function() {
var GITURL_REGEX = /^https:\/\/github.com\/([^\/]+)\/(.+).git$/;
var match = GITURL_REGEX.exec(getPackage().repository.url);
var git = {
owner: match[1],
repo: match[2]
};
return git;
},
getVersion: function(){
if (version) return version;
try {
var gitTag = getTagOfCurrentCommit();
var semVerVersion, codeName, fullVersion;
if (gitTag) {
// tagged release
fullVersion = semVerVersion = semver.valid(gitTag);
codeName = getTaggedReleaseCodeName(gitTag);
} else {
// snapshot release
semVerVersion = getSnapshotVersion();
fullVersion = semVerVersion + '-' + getSnapshotSuffix();
codeName = 'snapshot';
}
var versionParts = semVerVersion.match(/(\d+)\.(\d+)\.(\d+)/);
version = {
full: fullVersion,
major: versionParts[1],
minor: versionParts[2],
dot: versionParts[3],
codename: codeName,
cdn: getPackage().cdnVersion
};
// Stable versions have an even minor version
version.isStable = version.minor%2 === 0;
return version;
} catch (e) {
grunt.fail.warn(e);
}
function getTagOfCurrentCommit() {
var gitTagResult = shell.exec('git describe --exact-match', {silent:true});
var gitTagOutput = gitTagResult.output.trim();
var branchVersionPattern = new RegExp(getPackage().branchVersion.replace('.', '\\.').replace('*', '\\d+'));
if (gitTagResult.code === 0 && gitTagOutput.match(branchVersionPattern)) {
return gitTagOutput;
} else {
return null;
}
}
function getTaggedReleaseCodeName(tagName) {
var tagMessage = shell.exec('git cat-file -p '+ tagName +' | grep "codename"', {silent:true}).output;
var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
if (!codeName) {
throw new Error("Could not extract release code name. The message of tag "+tagName+
" must match '*codename(some release name)*'");
}
return codeName;
}
function getSnapshotVersion() {
var oldTags = shell.exec('git tag -l v'+getPackage().branchVersion, {silent:true}).output.trim().split('\n');
// ignore non semver versions.
oldTags = oldTags.filter(function(version) {
return version && semver.valid(version);
});
if (oldTags.length) {
oldTags.sort(semver.compare);
semVerVersion = oldTags[oldTags.length-1];
if (semVerVersion.indexOf('-') !== -1) {
semVerVersion = semver.inc(semVerVersion, 'prerelease');
} else {
semVerVersion = semver.inc(semVerVersion, 'patch');
}
} else {
semVerVersion = semver.valid(getPackage().branchVersion.replace(/\*/g, '0'));
}
return semVerVersion;
}
function getSnapshotSuffix() {
var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER || 'local';
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
return 'build.'+jenkinsBuild+'+sha.'+hash;
}
},
getPreviousVersions: function() {
var VERSION_REGEX = /([1-9]\d*)\.(\d+)\.(\d+)(?:-?rc\.?(\d+)|-(snapshot))?/;
// Pad out a number with zeros at the front to make it `digits` characters long
function pad(num, digits) {
var zeros = Array(digits+1).join('0');
return (zeros+num).slice(-digits);
}
function padVersion(version) {
// We pad out the version numbers with 0s so they sort nicely
// - Non-Release Candidates get 9999 for their release candidate section to make them appear earlier
// - Snapshots get 9 added to the front to move them to the top of the list
var maxLength = 4;
var padded = (version.snapshot ? '9' : '0') + pad(version.major, maxLength) +
pad(version.minor, maxLength) + pad(version.dot, maxLength) +
pad(version.rc || 9999, maxLength);
return padded;
}
function getVersionFromTag(tag) {
var match = VERSION_REGEX.exec(tag);
if ( match ) {
var version = {
tag: tag,
major: match[1], minor: match[2], dot: match[3], rc: match[4],
snapshot: !!match[5] && getSnapshotSuffix()
};
if(version.snapshot) {
version.full = version.major + '.' + version.minor + '.x (edge)';
} else {
version.full = version.major + '.' + version.minor + '.' + version.dot +
(version.rc ? '-rc.' + version.rc : '');
}
// Stable versions have an even minor version and are not a release candidate
version.isStable = !(version.minor%2 || version.rc);
// Versions before 1.0.2 had a different docs folder name
version.docsUrl = 'http://code.angularjs.org/' + version.full + '/docs';
if ( version.major < 1 || (version.major === 1 && version.minor === 0 && version.dot < 2 ) ) {
version.docsUrl += '-' + version.full;
}
return version;
}
}
var tags = shell.exec('git tag', {silent: true}).output.split(/\s*\n\s*/);
return _(tags)
.map(getVersionFromTag)
.filter() // getVersion can map to undefined - this clears those out
.sortBy(padVersion)
.value();
},
startKarma: function(config, singleRun, done){
var browsers = grunt.option('browsers');
var reporters = grunt.option('reporters');
@@ -225,7 +55,7 @@ module.exports = {
},
updateWebdriver: function(done){
updateWebdriver: function(done){
if (process.env.TRAVIS) {
// Skip the webdriver-manager update on Travis, since the browsers will
// be provided remotely.
@@ -319,9 +149,9 @@ module.exports = {
.replace(/"NG_VERSION_FULL"/g, NG_VERSION.full)
.replace(/"NG_VERSION_MAJOR"/, NG_VERSION.major)
.replace(/"NG_VERSION_MINOR"/, NG_VERSION.minor)
.replace(/"NG_VERSION_DOT"/, NG_VERSION.dot)
.replace(/"NG_VERSION_DOT"/, NG_VERSION.patch)
.replace(/"NG_VERSION_CDN"/, NG_VERSION.cdn)
.replace(/"NG_VERSION_CODENAME"/, NG_VERSION.codename);
.replace(/"NG_VERSION_CODENAME"/, NG_VERSION.codeName);
if (strict !== false) processed = this.singleStrict(processed, '\n\n', true);
return processed;
},
@@ -374,7 +204,7 @@ module.exports = {
var mapFile = minFile + '.map';
var mapFileName = mapFile.match(/[^\/]+$/)[0];
var errorFileName = file.replace(/\.js$/, '-errors.json');
var versionNumber = this.getVersion().full;
var versionNumber = grunt.config('NG_VERSION').full;
shell.exec(
'java ' +
this.java32flags() + ' ' +
+182
View File
@@ -0,0 +1,182 @@
var fs = require('fs');
var path = require('path');
var shell = require('shelljs');
var semver = require('semver');
var _ = require('lodash');
var currentPackage, previousVersions;
/**
* Load information about this project from the package.json
* @return {Object} The package information
*/
var getPackage = function() {
// Search up the folder hierarchy for the first package.json
var packageFolder = path.resolve('.');
while ( !fs.existsSync(path.join(packageFolder, 'package.json')) ) {
var parent = path.dirname(packageFolder);
if ( parent === packageFolder) { break; }
packageFolder = parent;
}
return JSON.parse(fs.readFileSync(path.join(packageFolder,'package.json'), 'UTF-8'));
};
/**
* Parse the github URL for useful information
* @return {Object} An object containing the github owner and repository name
*/
var getGitRepoInfo = function() {
var GITURL_REGEX = /^https:\/\/github.com\/([^\/]+)\/(.+).git$/;
var match = GITURL_REGEX.exec(currentPackage.repository.url);
var git = {
owner: match[1],
repo: match[2]
};
return git;
};
/**
* Extract the code name from the tagged commit's message - it should contain the text of the form:
* "codename(some-code-name)"
* @param {String} tagName Name of the tag to look in for the codename
* @return {String} The codename if found, otherwise null/undefined
*/
var getCodeName = function(tagName) {
var tagMessage = shell.exec('git cat-file -p '+ tagName +' | grep "codename"', {silent:true}).output;
var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
if (!codeName) {
throw new Error("Could not extract release code name. The message of tag "+tagName+
" must match '*codename(some release name)*'");
}
return codeName;
};
/**
* Compute a build segment for the version, from the Jenkins build number and current commit SHA
* @return {String} The build segment of the version
*/
function getBuild() {
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
return 'sha.'+hash;
}
/**
* If the current commit is tagged as a version get that version
* @return {SemVer} The version or null
*/
var getTaggedVersion = function() {
var gitTagResult = shell.exec('git describe --exact-match', {silent:true});
if ( gitTagResult.code === 0 ) {
var tag = gitTagResult.output.trim();
var version = semver.parse(tag);
if ( version && semver.satisfies(version, currentPackage.branchVersion)) {
version.codeName = getCodeName(tag);
version.full = version.version;
return version;
}
}
return null;
};
/**
* Stable versions have an even minor version and have no prerelease
* @param {SemVer} version The version to test
* @return {Boolean} True if the version is stable
*/
var isStable = function(version) {
return semver.satisfies(version, '1.0 || 1.2') && version.prerelease.length === 0;
};
/**
* Get a collection of all the previous versions sorted by semantic version
* @return {Array.<SemVer>} The collection of previous versions
*/
var getPreviousVersions = function() {
// always use the remote tags as the local clone might
// not contain all commits when cloned with git clone --depth=...
// Needed e.g. for Travis
var repo_url = currentPackage.repository.url;
var tagResults = shell.exec('git ls-remote --tags ' + repo_url + ' | grep -o -e "v[0-9].*[0-9]$"',
{silent: true});
if ( tagResults.code === 0 ) {
return _(tagResults.output.trim().split('\n'))
.map(function(tag) {
var version = semver.parse(tag);
return version;
})
.filter()
.map(function(version) {
version.isStable = isStable(version);
version.docsUrl = 'http://code.angularjs.org/' + version.version + '/docs';
// Versions before 1.0.2 had a different docs folder name
if ( version.major < 1 || (version.major === 1 && version.minor === 0 && version.dot < 2 ) ) {
version.docsUrl += '-' + version.version;
}
return version;
})
.sort(semver.compare)
.value();
} else {
return [];
}
};
/**
* Get the unstable snapshot version
* @return {SemVer} The snapshot version
*/
var getSnapshotVersion = function() {
version = _(previousVersions)
.filter(function(tag) {
return semver.satisfies(tag, currentPackage.branchVersion);
})
.last();
if ( !version ) {
// a snapshot version before the first tag on the branch
version = semver(currentPackage.branchVersion.replace('*','0-beta.1'));
}
// We need to clone to ensure that we are not modifying another version
version = semver(version.raw);
var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER;
if (!version.prerelease || !version.prerelease.length) {
// last release was a non beta release. Increment the patch level to
// indicate the next release that we will be doing.
// E.g. last release was 1.3.0, then the snapshot will be
// 1.3.1-build.1, which is lesser than 1.3.1 accorind the semver!
// If the last release was a beta release we don't update the
// beta number by purpose, as otherwise the semver comparison
// does not work any more when the next beta is released.
// E.g. don't generate 1.3.0-beta.2.build.1
// as this is bigger than 1.3.0-beta.2 according to semver
version.patch++;
}
version.prerelease = jenkinsBuild ? ['build', jenkinsBuild] : ['local'];
version.build = getBuild();
version.codeName = 'snapshot';
version.isSnapshot = true;
version.format();
version.full = version.version + '+' + version.build;
return version;
};
exports.currentPackage = currentPackage = getPackage();
exports.gitRepoInfo = gitRepoInfo = getGitRepoInfo();
exports.previousVersions = previousVersions = getPreviousVersions();
exports.currentVersion = getTaggedVersion() || getSnapshotVersion();
+2808
View File
File diff suppressed because it is too large Load Diff
+7 -5
View File
@@ -16,7 +16,7 @@
"grunt-contrib-jshint": "~0.7.2",
"grunt-ddescribe-iit": "~0.0.1",
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
"grunt-jscs-checker": "~0.3.2",
"grunt-jscs-checker": "~0.4.0",
"grunt-merge-conflict": "~0.0.1",
"grunt-parallel": "~0.3.1",
"grunt-shell": "~0.4.0",
@@ -27,7 +27,7 @@
"q-io": "~1.10.6",
"qq": "~0.3.5",
"shelljs": "~0.2.6",
"karma": "0.11.12",
"karma": "^0.12.0",
"karma-jasmine": "0.1.5",
"karma-chrome-launcher": "0.1.2",
"karma-firefox-launcher": "0.1.3",
@@ -50,10 +50,12 @@
"gulp-concat": "~2.1.7",
"canonical-path": "0.0.2",
"winston": "~0.7.2",
"dgeni": "~0.2.0",
"dgeni-packages": "^0.3.0",
"dgeni": "^0.2.2",
"dgeni-packages": "^0.8.1",
"gulp-jshint": "~1.4.2",
"jshint-stylish": "~0.1.5"
"jshint-stylish": "~0.1.5",
"node-html-encoder": "0.0.2",
"sorted-object": "^1.0.0"
},
"licenses": [
{
+18 -9
View File
@@ -11,8 +11,11 @@ ARG_DEFS=(
)
function init {
TMP_DIR=$(resolveDir ../../tmp)
BASE_DIR=$(resolveDir ../..)
TMP_DIR=$BASE_DIR/tmp
REPO_DIR=$TMP_DIR/angularjs.org
BRANCH_PATTERN=$(readJsonProp "$BASE_DIR/package.json" "branchVersion")
BUILD_DIR=$BASE_DIR/build
}
function prepare {
@@ -24,18 +27,24 @@ function prepare {
#
echo "-- Updating angularjs.org"
cd $REPO_DIR
VERSION_REGEX="[a-z0-9\-\.\+]+"
VERSION_REGEX="[-a-z0-9\.\+]+"
replaceInFile "index.html" "(ajax\/libs\/angularjs\/)$VERSION_REGEX" "\1$CDN_VERSION"
replaceInFile "index.html" "(<span class=\"version\">[^<]*<span>)$VERSION_REGEX" "\1$CDN_VERSION"
replaceInFile "index.html" "(code.angularjs.org\/)$VERSION_REGEX" "\1$CDN_VERSION"
# Replace the version in the script links that reference the Google CDN
# e.g. <script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.0-beta.1/angular.js"></script>
replaceInFile "index.html" "(http:\/\/ajax.googleapis.com\/ajax\/libs\/angularjs\/)$VERSION_REGEX" "\1$CDN_VERSION"
replaceInFile "js/homepage.js" "($scope.CURRENT_STABLE_VERSION[ ]*=[ ]*')$VERSION_REGEX" "\1$CDN_VERSION"
replaceInFile "js/homepage.js" "($scope.CURRENT_UNSTABLE_VERSION[ ]*=[ ]*')$VERSION_REGEX" "\1$CDN_VERSION"
# Replace the version in the script links that reference code.angularjs.org
# e.g. <script src="http://code.angularjs.org/1.3.0-beta.1/i18n/angular-locale_sk.js"></script>
replaceInFile "index.html" "(code\.angularjs\.org\/)$VERSION_REGEX" "\1$CDN_VERSION"
# Replace the version of the branch that we are updating
echo $BRANCH_PATTERN
echo $CDN_VERSION
replaceInFile "js/download-data.js" "branch:[ ]+'($BRANCH_PATTERN)',[ ]+version:[ ]+'$VERSION_REGEX'" "branch: '\1', version: '$CDN_VERSION'"
git add index.html
git add js/homepage.js
git commit -m "update(version): update angular version to $CDN_VERSION"
git add js/download-data.js
git commit -m "update(version): update angular version to $CDN_VERSION for branch $BRANCH_PATTERN"
}
function publish {
+45
View File
@@ -0,0 +1,45 @@
#!/usr/bin/env node
/**
* this script is just a temporary solution to deal with the issue of npm outputting the npm
* shrinkwrap file in an unstable manner.
*
* See: https://github.com/npm/npm/issues/3581
*/
var _ = require('lodash');
var sorted = require('sorted-object');
var fs = require('fs');
function cleanModule(module, name) {
// keep `from` and `resolve` properties for git dependencies, delete otherwise
if (!(module.resolved && module.resolved.match(/^git:\/\//))) {
delete module.from;
delete module.resolved;
}
if (name === 'chokidar') {
if (module.version === '0.8.1') {
delete module.dependencies;
} else {
throw new Error("Unfamiliar chokidar version (v" + module.version +
") , please check status of https://github.com/paulmillr/chokidar/pull/106");
}
}
_.forEach(module.dependencies, function(mod, name) {
cleanModule(mod, name);
});
}
console.log('Reading npm-shrinkwrap.json');
var shrinkwrap = require('./../npm-shrinkwrap.json');
console.log('Cleaning shrinkwrap object');
cleanModule(shrinkwrap, shrinkwrap.name);
console.log('Writing cleaned npm-shrinkwrap.json');
fs.writeFileSync('./npm-shrinkwrap.json', JSON.stringify(sorted(shrinkwrap), null, 2) + "\n");
+26 -8
View File
@@ -55,19 +55,37 @@ function prepare {
git commit -m "v$NEW_VERSION"
}
function publish {
if [[ $IS_SNAPSHOT_BUILD ]]; then
echo "-- Updating snapshot version"
curl -G --data-urlencode "ver=$NEW_VERSION" http://code.angularjs.org/fetchLatestSnapshot.php
exit 0;
fi
function _update_snapshot() {
for backend in "$@" ; do
echo "-- Updating snapshot version: backend=$backend"
curl -G --data-urlencode "ver=$NEW_VERSION" http://$backend:8003/fetchLatestSnapshot.php
done
}
function _update_code() {
cd $REPO_DIR
echo "-- Pushing code.angularjs.org"
git push origin master
echo "-- Refreshing code.angularjs.org"
curl http://code.angularjs.org/gitFetchSite.php
for backend in "$@" ; do
echo "-- Refreshing code.angularjs.org: backend=$backend"
curl http://$backend:8003/gitFetchSite.php
done
}
function publish {
# The TXT record for backends.angularjs.org is a CSV of the IP addresses for
# the currently serving Compute Engine backends.
# code.angularjs.org is served out of port 8003 on these backends.
backends=("$(dig backends.angularjs.org +short TXT | python -c 'print raw_input()[1:-1].replace(",", "\n")')")
if [[ $IS_SNAPSHOT_BUILD ]]; then
_update_snapshot ${backends[@]}
else
_update_code ${backends[@]}
fi
}
source $(dirname $0)/../utils.inc
+1 -1
View File
@@ -23,7 +23,7 @@ ARG_DEFS=(
)
function init {
if [[ $(git rev-parse --short HEAD) != $COMMIT_SHA ]]; then
if [[ $(git rev-parse HEAD) != $(git rev-parse $COMMIT_SHA) ]]; then
echo "HEAD is not at $COMMIT_SHA"
usage
fi
+3 -3
View File
@@ -15,7 +15,7 @@
# 0. Set the current directory to the directory of the script. By this
# the script can be called from anywhere.
# 1. Parse the named arguments
# 2. If the parameter "git_push_dryrun" is set, all calls the `git push` in this script
# 2. If the parameter "git_push_dryrun" is set, all calls to `git push` in this script
# or in child scripts will be intercepted so that the `--dry-run` and `--porcelain` is added
# to show what the push would do but not actually do it.
# 3. If the parameter "verbose" is set, the `-x` flag will be set in bash.
@@ -36,7 +36,7 @@
# with the name of the parameter in upper case (with dash converted to underscore).
#
# Special arguments that are always available:
# - "--action=.*": This parameter will be used to dispatch to a function with that name when the
# - "--action=.*": This parameter will be used to execute a function with that name when the
# script is started
# - "--git_push_dryrun=true": This will intercept all calls to `git push` in this script
# or in child scripts so that the `--dry-run` and `--porcelain` is added
@@ -195,7 +195,7 @@ function isFunction {
}
# readJsonProp(jsonFile, property)
# - restriction: property needs to be on an own line!
# - restriction: property needs to be on a single line!
function readJsonProp {
echo $(sed -En 's/.*"'$2'"[ ]*:[ ]*"(.*)".*/\1/p' $1)
}
+1
View File
@@ -64,6 +64,7 @@
"isWindow": false,
"isScope": false,
"isFile": false,
"isBlob": false,
"isBoolean": false,
"trim": false,
"isElement": false,
+41
View File
@@ -45,6 +45,7 @@
-isWindow,
-isScope,
-isFile,
-isBlob,
-isBoolean,
-trim,
-isElement,
@@ -566,6 +567,11 @@ function isFile(obj) {
}
function isBlob(obj) {
return toString.call(obj) === '[object Blob]';
}
function isBoolean(value) {
return typeof value === 'boolean';
}
@@ -1235,6 +1241,41 @@ function angularInit(element, bootstrap) {
* Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually.
* They must use {@link ng.directive:ngApp ngApp}.
*
* Angular will detect if it has been loaded into the browser more than once and only allow the
* first loaded script to be bootstrapped and will report a warning to the browser console for
* each of the subsequent scripts. This prevents strange results in applications, where otherwise
* multiple instances of Angular try to work on the DOM.
*
* <example name="multi-bootstrap" module="multi-bootstrap">
* <file name="index.html">
* <script src="../../../angular.js"></script>
* <div ng-controller="BrokenTable">
* <table>
* <tr>
* <th ng-repeat="heading in headings">{{heading}}</th>
* </tr>
* <tr ng-repeat="filling in fillings">
* <td ng-repeat="fill in filling">{{fill}}</td>
* </tr>
* </table>
* </div>
* </file>
* <file name="controller.js">
* var app = angular.module('multi-bootstrap', [])
*
* .controller('BrokenTable', function($scope) {
* $scope.headings = ['One', 'Two', 'Three'];
* $scope.fillings = [[1, 2, 3], ['A', 'B', 'C'], [7, 8, 9]];
* });
* </file>
* <file name="protractor.js" type="protractor">
* it('should only insert one table cell for each item in $scope.fillings', function() {
* expect(element.all(by.css('td')).count())
* .toBe(9);
* });
* </file>
* </example>
*
* @param {Element} element DOM element which is the root of angular application.
* @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
* Each item in the array should be the name of a predefined module or a (DI annotated)
+6
View File
@@ -1,3 +1,9 @@
if (window.angular.bootstrap) {
//AngularJS is already loaded, so we can return here...
console.log('WARNING: Tried to load angular more than once.');
return;
}
//try to bind to jquery now so that one can write angular.element().read()
//but we will rebind on bootstrap again.
bindJQuery();
+7 -3
View File
@@ -364,11 +364,15 @@ function jqLiteInheritedData(element, name, value) {
var names = isArray(name) ? name : [name];
while (element.length) {
var node = element[0];
for (var i = 0, ii = names.length; i < ii; i++) {
if ((value = element.data(names[i])) !== undefined) return value;
}
element = element.parent();
// If dealing with a document fragment node with a host element, and no parent, use the host
// element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
// to lookup parent controllers.
element = jqLite(node.parentNode || (node.nodeType === 11 && node.host));
}
}
@@ -457,7 +461,7 @@ forEach({
return jqLite(element).data('$isolateScope') || jqLite(element).data('$isolateScopeNoTemplate');
},
controller: jqLiteController ,
controller: jqLiteController,
injector: function(element) {
return jqLiteInheritedData(element, '$injector');
+2 -4
View File
@@ -55,10 +55,10 @@ function setupModuleLoader(window) {
* myModule.value('appName', 'MyCoolApp');
*
* // configure existing services inside initialization blocks.
* myModule.config(function($locationProvider) {
* myModule.config(['$locationProvider', function($locationProvider) {
* // Configure existing providers
* $locationProvider.hashPrefix('!');
* });
* }]);
* ```
*
* Then you can create an injector and load your modules like this:
@@ -114,7 +114,6 @@ function setupModuleLoader(window) {
* @ngdoc property
* @name angular.Module#requires
* @module ng
* @propertyOf angular.Module
* @returns {Array.<string>} List of module names which must be loaded before this module.
* @description
* Holds the list of modules which the injector will load before the current module is
@@ -126,7 +125,6 @@ function setupModuleLoader(window) {
* @ngdoc property
* @name angular.Module#name
* @module ng
* @propertyOf angular.Module
* @returns {string} Name of the module.
* @description
*/
+1 -1
View File
@@ -194,7 +194,6 @@ function Browser(window, document, $log, $sniffer) {
/**
* @name $browser#onUrlChange
* @TODO(vojta): refactor to use node's syntax for events
*
* @description
* Register callback function that will be called, when url changes.
@@ -215,6 +214,7 @@ function Browser(window, document, $log, $sniffer) {
* @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
*/
self.onUrlChange = function(callback) {
// TODO(vojta): refactor to use node's syntax for events
if (!urlChangeInit) {
// We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
// don't fire popstate when user change the address bar and don't fire hashchange when url
+4 -8
View File
@@ -208,15 +208,11 @@ function $CacheFactoryProvider() {
* `$templateCache` service directly.
*
* Adding via the `script` tag:
*
* ```html
* <html ng-app>
* <head>
* <script type="text/ng-template" id="templateId.html">
* This is the content of the template
* </script>
* </head>
* ...
* </html>
* <script type="text/ng-template" id="templateId.html">
* <p>This is the content of the template</p>
* </script>
* ```
*
* **Note:** the `script` tag containing the template does not need to be included in the `head` of
+8 -9
View File
@@ -503,7 +503,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
Suffix = 'Directive',
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
TABLE_CONTENT_REGEXP = /^<\s*(tr|th|td|tbody)(\s+[^>]*)?>/i;
TABLE_CONTENT_REGEXP = /^<\s*(tr|th|td|thead|tbody|tfoot)(\s+[^>]*)?>/i;
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
// The assumption is that future DOM event attribute names will begin with
@@ -1649,16 +1649,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
template = trim(template);
if ((type = TABLE_CONTENT_REGEXP.exec(template))) {
type = type[1].toLowerCase();
var table = jqLite('<table>' + template + '</table>'),
tbody = table.children('tbody'),
leaf = /(td|th)/.test(type) && table.find('tr');
if (tbody.length && type !== 'tbody') {
table = tbody;
var table = jqLite('<table>' + template + '</table>');
if (/(thead|tbody|tfoot)/.test(type)) {
return table.children(type);
}
if (leaf && leaf.length) {
table = leaf;
table = table.children('tbody');
if (type === 'tr') {
return table.children('tr');
}
return table.contents();
return table.children('tr').contents();
}
return jqLite('<div>' +
template +
+1 -1
View File
@@ -277,7 +277,7 @@
* such as selected. (Their presence means true and their absence means false.)
* If we put an Angular interpolation expression into such an attribute then the
* binding information would be lost when the browser removes the attribute.
* The `ngSelected` directive solves this problem for the `selected` atttribute.
* The `ngSelected` directive solves this problem for the `selected` attribute.
* This complementary directive is not removed by the browser and so provides
* a permanent reliable place to store the binding information.
*
-2
View File
@@ -360,8 +360,6 @@ 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
@@ -13,7 +13,7 @@
* Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
* `{{ expression }}` which is similar but less verbose.
*
* It is preferrable to use `ngBind` instead of `{{ expression }}` when a template is momentarily
* It is preferable to use `ngBind` instead of `{{ expression }}` when a template is momentarily
* displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
* element attribute, it makes the bindings invisible to the user while the page is loading.
*
+1 -3
View File
@@ -32,7 +32,7 @@
* @priority 400
*
* @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
* make sure you wrap it in quotes, e.g. `src="'myPartialTemplate.html'"`.
* make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
* @param {string=} onload Expression to evaluate when a new partial is loaded.
*
* @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
@@ -146,7 +146,6 @@
/**
* @ngdoc event
* @name ngInclude#$includeContentRequested
* @eventOf ng.directive:ngInclude
* @eventType emit on the scope ngInclude was declared in
* @description
* Emitted every time the ngInclude content is requested.
@@ -156,7 +155,6 @@
/**
* @ngdoc event
* @name ngInclude#$includeContentLoaded
* @eventOf ng.directive:ngInclude
* @eventType emit on the current ngInclude scope
* @description
* Emitted every time the ngInclude content is reloaded.
+5 -3
View File
@@ -68,9 +68,11 @@
* as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
*
* @animations
* enter - when a new item is added to the list or when an item is revealed after a filter
* leave - when an item is removed from the list or when an item is filtered out
* move - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
* **.enter** - when a new item is added to the list or when an item is revealed after a filter
*
* **.leave** - when an item is removed from the list or when an item is filtered out
*
* **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
*
* @element ANY
* @scope
-1
View File
@@ -41,7 +41,6 @@
* @scope
* @priority 800
* @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
* @paramDescription
* On child elements add:
*
* * `ngSwitchWhen`: the case statement to match against. If match then this
+7 -1
View File
@@ -394,6 +394,12 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
value = valueFn(scope, locals);
}
}
// Update the null option's selected property here so $render cleans it up correctly
if (optionGroupsCache[0].length > 1) {
if (optionGroupsCache[0][1].id !== key) {
optionGroupsCache[0][1].selected = false;
}
}
}
ctrl.$setViewValue(value);
});
@@ -531,7 +537,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
lastElement.val(existingOption.id = option.id);
}
// lastElement.prop('selected') provided by jQuery has side-effects
if (lastElement[0].selected !== option.selected) {
if (existingOption.selected !== option.selected) {
lastElement.prop('selected', (existingOption.selected = option.selected));
}
} else {
+16
View File
@@ -7,6 +7,22 @@
*
* @description
* A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
*
* @example
<example>
<file name="index.html">
<div ng-controller="MainCtrl">
<p>$document title: <b ng-bind="title"></b></p>
<p>window.document title: <b ng-bind="windowTitle"></b></p>
</div>
</file>
<file name="script.js">
function MainCtrl($scope, $document) {
$scope.title = $document[0].title;
$scope.windowTitle = angular.element(window.document)[0].title;
}
</file>
</example>
*/
function $DocumentProvider(){
this.$get = ['$window', function(window){
+1 -1
View File
@@ -441,7 +441,7 @@ function dateFilter($locale) {
* @returns {string} JSON string.
*
*
* @example:
* @example
<example>
<file name="index.html">
<pre>{{ {'name':'value'} | json }}</pre>
+6
View File
@@ -74,6 +74,12 @@ function orderByFilter($parse){
predicate = predicate.substring(1);
}
get = $parse(predicate);
if (get.constant) {
var key = get();
return reverseComparator(function(a,b) {
return compare(a[key], b[key]);
}, descending);
}
}
return reverseComparator(function(a,b){
return compare(get(a),get(b));
+3 -4
View File
@@ -103,7 +103,7 @@ function $HttpProvider() {
// transform outgoing request data
transformRequest: [function(d) {
return isObject(d) && !isFile(d) ? toJson(d) : d;
return isObject(d) && !isFile(d) && !isBlob(d) ? toJson(d) : d;
}],
// default headers
@@ -236,9 +236,8 @@ function $HttpProvider() {
*
* # Shortcut methods
*
* Since all invocations of the $http service require passing in an HTTP method and URL, and
* POST/PUT requests require request data to be provided as well, shortcut methods
* were created:
* Shortcut methods are also available. All shortcut methods require passing in the URL, and
* request data must be passed in for POST/PUT requests.
*
* ```js
* $http.get('/someUrl').success(successCallback);
+5 -3
View File
@@ -144,9 +144,11 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
jsonpDone = xhr = null;
// fix status code when it is 0 (0 status is undocumented).
// Occurs when accessing file resources.
// On Android 4.1 stock browser it occurs while retrieving files from application cache.
status = (status === 0) ? (response ? 200 : 404) : status;
// Occurs when accessing file resources or on Android 4.1 stock browser
// while retrieving files from application cache.
if (status === 0) {
status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
}
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
status = status == 1223 ? 204 : status;
+1 -1
View File
@@ -112,7 +112,7 @@
*
* Because `finally` is a reserved word in JavaScript and reserved keywords are not supported as
* property names by ES3, you'll need to invoke the method like `promise['finally'](callback)` to
* make your code IE8 compatible.
* make your code IE8 and Android 2.x compatible.
*
* # Chaining promises
*
+21 -10
View File
@@ -1,21 +1,32 @@
'use strict';
function $$RAFProvider(){ //rAF
this.$get = ['$window', function($window) {
this.$get = ['$window', '$timeout', function($window, $timeout) {
var requestAnimationFrame = $window.requestAnimationFrame ||
$window.webkitRequestAnimationFrame;
$window.webkitRequestAnimationFrame ||
$window.mozRequestAnimationFrame;
var cancelAnimationFrame = $window.cancelAnimationFrame ||
$window.webkitCancelAnimationFrame;
$window.webkitCancelAnimationFrame ||
$window.mozCancelAnimationFrame ||
$window.webkitCancelRequestAnimationFrame;
var raf = function(fn) {
var id = requestAnimationFrame(fn);
return function() {
cancelAnimationFrame(id);
};
};
var rafSupported = !!requestAnimationFrame;
var raf = rafSupported
? function(fn) {
var id = requestAnimationFrame(fn);
return function() {
cancelAnimationFrame(id);
};
}
: function(fn) {
var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
return function() {
$timeout.cancel(timer);
};
};
raf.supported = !!requestAnimationFrame;
raf.supported = rafSupported;
return raf;
}];
+46 -11
View File
@@ -139,7 +139,6 @@ function $RootScopeProvider(){
/**
* @ngdoc property
* @name $rootScope.Scope#$id
* @propertyOf ng.$rootScope.Scope
* @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for
* debugging.
*/
@@ -399,30 +398,40 @@ function $RootScopeProvider(){
* {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
* collection will trigger a call to the `listener`.
*
* @param {function(newCollection, oldCollection, scope)} listener a callback function that is
* fired with both the `newCollection` and `oldCollection` as parameters.
* The `newCollection` object is the newly modified data obtained from the `obj` expression
* and the `oldCollection` object is a copy of the former collection data.
* The `scope` refers to the current scope.
* @param {function(newCollection, oldCollection, scope)} listener a callback function called
* when a change is detected.
* - The `newCollection` object is the newly modified data obtained from the `obj` expression
* - The `oldCollection` object is a copy of the former collection data.
* Due to performance considerations, the`oldCollection` value is computed only if the
* `listener` function declares two or more arguments.
* - The `scope` argument refers to the current scope.
*
* @returns {function()} Returns a de-registration function for this listener. When the
* de-registration function is executed, the internal watch operation is terminated.
*/
$watchCollection: function(obj, listener) {
var self = this;
var oldValue;
// the current value, updated on each dirty-check run
var newValue;
// a shallow copy of the newValue from the last dirty-check run,
// updated to match newValue during dirty-check run
var oldValue;
// a shallow copy of the newValue from when the last change happened
var veryOldValue;
// only track veryOldValue if the listener is asking for it
var trackVeryOldValue = (listener.length > 1);
var changeDetected = 0;
var objGetter = $parse(obj);
var internalArray = [];
var internalObject = {};
var initRun = true;
var oldLength = 0;
function $watchCollectionWatch() {
newValue = objGetter(self);
var newLength, key;
if (!isObject(newValue)) {
if (!isObject(newValue)) { // if primitive
if (oldValue !== newValue) {
oldValue = newValue;
changeDetected++;
@@ -444,7 +453,9 @@ function $RootScopeProvider(){
}
// copy the items to oldValue and look for changes.
for (var i = 0; i < newLength; i++) {
if (oldValue[i] !== newValue[i]) {
var bothNaN = (oldValue[i] !== oldValue[i]) &&
(newValue[i] !== newValue[i]);
if (!bothNaN && (oldValue[i] !== newValue[i])) {
changeDetected++;
oldValue[i] = newValue[i];
}
@@ -488,7 +499,32 @@ function $RootScopeProvider(){
}
function $watchCollectionAction() {
listener(newValue, oldValue, self);
if (initRun) {
initRun = false;
listener(newValue, newValue, self);
} else {
listener(newValue, veryOldValue, self);
}
// make a copy for the next time a collection is changed
if (trackVeryOldValue) {
if (!isObject(newValue)) {
//primitive
veryOldValue = newValue;
} else if (isArrayLike(newValue)) {
veryOldValue = new Array(newValue.length);
for (var i = 0; i < newValue.length; i++) {
veryOldValue[i] = newValue[i];
}
} else { // if object
veryOldValue = {};
for (var key in newValue) {
if (hasOwnProperty.call(newValue, key)) {
veryOldValue[key] = newValue[key];
}
}
}
}
}
return this.$watch($watchCollectionWatch, $watchCollectionAction);
@@ -652,7 +688,6 @@ function $RootScopeProvider(){
/**
* @ngdoc event
* @name $rootScope.Scope#$destroy
* @eventOf ng.$rootScope.Scope
* @eventType broadcast on scope being destroyed
*
* @description
+1 -1
View File
@@ -521,7 +521,7 @@ function $SceDelegateProvider() {
* |---------------------|----------------|
* | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. |
* | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
* | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't consititute an SCE context. |
* | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. |
* | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contens are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
* | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. |
*
+3 -4
View File
@@ -701,8 +701,7 @@ angular.module('ngAnimate', ['ng'])
/**
*
* @ngdoc function
* @name ng.$animate#setClass
* @methodOf ng.$animate
* @name $animate#setClass
* @function
* @description Adds and/or removes the given CSS classes to and from the element.
* Once complete, the done() callback will be fired (if provided).
@@ -771,7 +770,7 @@ angular.module('ngAnimate', ['ng'])
fireDOMOperation();
fireBeforeCallbackAsync();
fireAfterCallbackAsync();
fireDoneCallbackAsync();
closeAnimation();
return;
}
@@ -950,7 +949,7 @@ angular.module('ngAnimate', ['ng'])
animation, but class-based animations don't. An example of this
failing would be when a parent HTML tag has a ng-class attribute
causing ALL directives below to skip animations during the digest */
if(runner.isClassBased) {
if(runner && runner.isClassBased) {
cleanup(element, className);
} else {
$$asyncCallback(function() {
+7 -8
View File
@@ -25,8 +25,9 @@ angular.module('ngCookies', ['ng']).
* @description
* Provides read/write access to browser's cookies.
*
* Only a simple Object is exposed and by adding or removing properties to/from
* this object, new cookies are created/deleted at the end of current $eval.
* Only a simple Object is exposed and by adding or removing properties to/from this object, new
* cookies are created/deleted at the end of current $eval.
* The object's properties can only be strings.
*
* Requires the {@link ngCookies `ngCookies`} module to be installed.
*
@@ -94,12 +95,10 @@ angular.module('ngCookies', ['ng']).
for(name in cookies) {
value = cookies[name];
if (!angular.isString(value)) {
if (angular.isDefined(lastCookies[name])) {
cookies[name] = lastCookies[name];
} else {
delete cookies[name];
}
} else if (value !== lastCookies[name]) {
value = '' + value;
cookies[name] = value;
}
if (value !== lastCookies[name]) {
$browser.cookies(name, value);
updated = true;
}
+2 -2
View File
@@ -1821,8 +1821,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
* an array containing response status (number), response data (string) and response headers
* (Object).
* - passThrough `{function()}` Any request matching a backend definition with `passThrough`
* handler, will be pass through to the real backend (an XHR request will be made to the
* server.
* handler will be passed through to the real backend (an XHR request will be made to the
* server.)
*/
/**
+11 -1
View File
@@ -253,7 +253,7 @@ function shallowClearAndCopy(src, dst) {
```js
var User = $resource('/user/:userId', {userId:'@id'});
var user = User.get({userId:123}, function() {
User.get({userId:123}, function(user) {
user.abc = true;
user.$save();
});
@@ -273,6 +273,16 @@ function shallowClearAndCopy(src, dst) {
});
});
```
*
* You can also access the raw `$http` promise via the `$promise` property on the object returned
*
```
var User = $resource('/user/:userId', {userId:'@id'});
User.get({userId:123})
.$promise.then(function(user) {
$scope.user = user;
});
```
* # Creating a custom 'PUT' request
* In this example we create a custom method on our resource to make a PUT request
+31 -30
View File
@@ -119,38 +119,39 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
</file>
<file name="script.js">
angular.module('ngViewExample', ['ngRoute', 'ngAnimate'],
function($routeProvider, $locationProvider) {
$routeProvider.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: BookCtrl,
controllerAs: 'book'
});
$routeProvider.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
controller: ChapterCtrl,
controllerAs: 'chapter'
});
angular.module('ngViewExample', ['ngRoute', 'ngAnimate'])
.config(['$routeProvider', '$locationProvider',
function($routeProvider, $locationProvider) {
$routeProvider
.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: 'BookCtrl',
controllerAs: 'book'
})
.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
controller: 'ChapterCtrl',
controllerAs: 'chapter'
});
// configure html5 to get links working on jsfiddle
$locationProvider.html5Mode(true);
});
// configure html5 to get links working on jsfiddle
$locationProvider.html5Mode(true);
}])
.controller('MainCtrl', ['$route', '$routeParams', '$location',
function($route, $routeParams, $location) {
this.$route = $route;
this.$location = $location;
this.$routeParams = $routeParams;
}])
.controller('BookCtrl', ['$routeParams', function($routeParams) {
this.name = "BookCtrl";
this.params = $routeParams;
}])
.controller('ChapterCtrl', ['$routeParams', function($routeParams) {
this.name = "ChapterCtrl";
this.params = $routeParams;
}]);
function MainCtrl($route, $routeParams, $location) {
this.$route = $route;
this.$location = $location;
this.$routeParams = $routeParams;
}
function BookCtrl($routeParams) {
this.name = "BookCtrl";
this.params = $routeParams;
}
function ChapterCtrl($routeParams) {
this.name = "ChapterCtrl";
this.params = $routeParams;
}
</file>
<file name="protractor.js" type="protractor">
+103 -99
View File
@@ -82,7 +82,7 @@ function $RouteProvider(){
*
* If `template` is a function, it will be called with the following parameters:
*
* - `{Array.&lt;Object&gt;}` - route parameters extracted from the current
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `templateUrl` `{string=|function()=}` path or function that returns a path to an html
@@ -90,7 +90,7 @@ function $RouteProvider(){
*
* If `templateUrl` is a function, it will be called with the following parameters:
*
* - `{Array.&lt;Object&gt;}` - route parameters extracted from the current
* - `{Array.<Object>}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
@@ -247,7 +247,7 @@ function $RouteProvider(){
* - `$scope` - The current route scope.
* - `$template` - The current route template HTML.
*
* @property {Array.<Object>} routes Array of all configured routes.
* @property {Object} routes Object with all route configuration Objects as its properties.
*
* @description
* `$route` is used for deep-linking URLs to controllers and views (HTML partials).
@@ -262,102 +262,106 @@ function $RouteProvider(){
* {@link ngRoute.$routeParams `$routeParams`} service.
*
* @example
This example shows how changing the URL hash causes the `$route` to match a route against the
URL, and the `ngView` pulls in the partial.
Note that this example is using {@link ng.directive:script inlined templates}
to get it working on jsfiddle as well.
<example name="$route-service" module="ngRouteExample" deps="angular-route.js" fixBase="true">
<file name="index.html">
<div ng-controller="MainCntl">
Choose:
<a href="Book/Moby">Moby</a> |
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
<a href="Book/Gatsby">Gatsby</a> |
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
<a href="Book/Scarlet">Scarlet Letter</a><br/>
<div ng-view></div>
<hr />
<pre>$location.path() = {{$location.path()}}</pre>
<pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
<pre>$route.current.params = {{$route.current.params}}</pre>
<pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
<pre>$routeParams = {{$routeParams}}</pre>
</div>
</file>
<file name="book.html">
controller: {{name}}<br />
Book Id: {{params.bookId}}<br />
</file>
<file name="chapter.html">
controller: {{name}}<br />
Book Id: {{params.bookId}}<br />
Chapter Id: {{params.chapterId}}
</file>
<file name="script.js">
angular.module('ngRouteExample', ['ngRoute'])
.config(function($routeProvider, $locationProvider) {
$routeProvider.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: BookCntl,
resolve: {
// I will cause a 1 second delay
delay: function($q, $timeout) {
var delay = $q.defer();
$timeout(delay.resolve, 1000);
return delay.promise;
}
}
});
$routeProvider.when('/Book/:bookId/ch/:chapterId', {
templateUrl: 'chapter.html',
controller: ChapterCntl
});
// configure html5 to get links working on jsfiddle
$locationProvider.html5Mode(true);
});
function MainCntl($scope, $route, $routeParams, $location) {
$scope.$route = $route;
$scope.$location = $location;
$scope.$routeParams = $routeParams;
}
function BookCntl($scope, $routeParams) {
$scope.name = "BookCntl";
$scope.params = $routeParams;
}
function ChapterCntl($scope, $routeParams) {
$scope.name = "ChapterCntl";
$scope.params = $routeParams;
}
</file>
<file name="protractor.js" type="protractor">
it('should load and compile correct template', function() {
element(by.linkText('Moby: Ch1')).click();
var content = element(by.css('[ng-view]')).getText();
expect(content).toMatch(/controller\: ChapterCntl/);
expect(content).toMatch(/Book Id\: Moby/);
expect(content).toMatch(/Chapter Id\: 1/);
element(by.partialLinkText('Scarlet')).click();
content = element(by.css('[ng-view]')).getText();
expect(content).toMatch(/controller\: BookCntl/);
expect(content).toMatch(/Book Id\: Scarlet/);
});
</file>
</example>
* This example shows how changing the URL hash causes the `$route` to match a route against the
* URL, and the `ngView` pulls in the partial.
*
* Note that this example is using {@link ng.directive:script inlined templates}
* to get it working on jsfiddle as well.
*
* <example name="$route-service" module="ngRouteExample"
* deps="angular-route.js" fixBase="true">
* <file name="index.html">
* <div ng-controller="MainController">
* Choose:
* <a href="Book/Moby">Moby</a> |
* <a href="Book/Moby/ch/1">Moby: Ch1</a> |
* <a href="Book/Gatsby">Gatsby</a> |
* <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
* <a href="Book/Scarlet">Scarlet Letter</a><br/>
*
* <div ng-view></div>
*
* <hr />
*
* <pre>$location.path() = {{$location.path()}}</pre>
* <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
* <pre>$route.current.params = {{$route.current.params}}</pre>
* <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
* <pre>$routeParams = {{$routeParams}}</pre>
* </div>
* </file>
*
* <file name="book.html">
* controller: {{name}}<br />
* Book Id: {{params.bookId}}<br />
* </file>
*
* <file name="chapter.html">
* controller: {{name}}<br />
* Book Id: {{params.bookId}}<br />
* Chapter Id: {{params.chapterId}}
* </file>
*
* <file name="script.js">
* angular.module('ngRouteExample', ['ngRoute'])
*
* .controller('MainController', function($scope, $route, $routeParams, $location) {
* $scope.$route = $route;
* $scope.$location = $location;
* $scope.$routeParams = $routeParams;
* })
*
* .controller('BookController', function($scope, $routeParams) {
* $scope.name = "BookController";
* $scope.params = $routeParams;
* })
*
* .controller('ChapterController', function($scope, $routeParams) {
* $scope.name = "ChapterController";
* $scope.params = $routeParams;
* })
*
* .config(function($routeProvider, $locationProvider) {
* $routeProvider
* .when('/Book/:bookId', {
* templateUrl: 'book.html',
* controller: 'BookController',
* resolve: {
* // I will cause a 1 second delay
* delay: function($q, $timeout) {
* var delay = $q.defer();
* $timeout(delay.resolve, 1000);
* return delay.promise;
* }
* }
* })
* .when('/Book/:bookId/ch/:chapterId', {
* templateUrl: 'chapter.html',
* controller: 'ChapterController'
* });
*
* // configure html5 to get links working on jsfiddle
* $locationProvider.html5Mode(true);
* });
*
* </file>
*
* <file name="protractor.js" type="protractor">
* it('should load and compile correct template', function() {
* element(by.linkText('Moby: Ch1')).click();
* var content = element(by.css('[ng-view]')).getText();
* expect(content).toMatch(/controller\: ChapterController/);
* expect(content).toMatch(/Book Id\: Moby/);
* expect(content).toMatch(/Chapter Id\: 1/);
*
* element(by.partialLinkText('Scarlet')).click();
*
* content = element(by.css('[ng-view]')).getText();
* expect(content).toMatch(/controller\: BookController/);
* expect(content).toMatch(/Book Id\: Scarlet/);
* });
* </file>
* </example>
*/
/**
+1 -1
View File
@@ -254,7 +254,7 @@ function htmlParser( html, handler ) {
match = html.match( DOCTYPE_REGEXP );
if ( match ) {
html = html.replace( match[0] , '');
html = html.replace( match[0], '');
chars = false;
}
// end tag
+15 -1
View File
@@ -53,6 +53,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
var ACTIVE_CLASS_NAME = 'ng-click-active';
var lastPreventedTime;
var touchCoordinates;
var lastLabelClickCoordinates;
// TAP EVENTS AND GHOST CLICKS
@@ -124,10 +125,23 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
var y = touches[0].clientY;
// Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label
// and on the input element). Depending on the exact browser, this second click we don't want
// to bust has either (0,0) or negative coordinates.
// to bust has either (0,0), negative coordinates, or coordinates equal to triggering label
// click event
if (x < 1 && y < 1) {
return; // offscreen
}
if (lastLabelClickCoordinates &&
lastLabelClickCoordinates[0] === x && lastLabelClickCoordinates[1] === y) {
return; // input click triggered by label click
}
// reset label click coordinates on first subsequent click
if (lastLabelClickCoordinates) {
lastLabelClickCoordinates = null;
}
// remember label click coordinates to prevent click busting of trigger click event on input
if (event.target.tagName.toLowerCase() === 'label') {
lastLabelClickCoordinates = [x, y];
}
// Look for an allowable region containing this click.
// If we find one, that means it was created by touchstart and not removed by
+1 -1
View File
@@ -163,7 +163,7 @@ describe('injector', function() {
function $f_n0 /*
*/(
$a, // x, <-- looks like an arg but it is a comment
b_ , /* z, <-- looks like an arg but it is a
b_, /* z, <-- looks like an arg but it is a
multi-line comment
function (a, b) {}
*/
+10 -4
View File
@@ -269,21 +269,27 @@ function provideLog($provide) {
log.toString = function() {
return messages.join('; ');
}
};
log.toArray = function() {
return messages;
}
};
log.reset = function() {
messages = [];
};
log.empty = function() {
var currentMessages = messages;
messages = [];
return currentMessages;
}
log.fn = function(msg) {
return function() {
log(msg);
}
}
};
};
log.$$log = true;
+13
View File
@@ -168,6 +168,19 @@ describe('jqLite', function() {
dealoc(ul);
});
it('should pass through DocumentFragment boundaries via host', function() {
var host = jqLite('<div></div>'),
frag = document.createDocumentFragment(),
$frag = jqLite(frag);
frag.host = host[0];
host.data("foo", 123);
host.append($frag);
expect($frag.inheritedData("foo")).toBe(123);
dealoc(host);
dealoc($frag);
});
});
+48
View File
@@ -529,10 +529,18 @@ describe('$compile', function() {
replace: true,
template: '<th>TH</th>'
}));
directive('replaceWithThead', valueFn({
replace: true,
template: '<thead><tr><td>TD</td></tr></thead>'
}));
directive('replaceWithTbody', valueFn({
replace: true,
template: '<tbody><tr><td>TD</td></tr></tbody>'
}));
directive('replaceWithTfoot', valueFn({
replace: true,
template: '<tfoot><tr><td>TD</td></tr></tfoot>'
}));
}));
@@ -718,12 +726,26 @@ describe('$compile', function() {
expect(nodeName_(element)).toMatch(/th/i);
}));
it('should support templates with root <thead> tags', inject(function($compile, $rootScope) {
expect(function() {
element = $compile('<div replace-with-thead></div>')($rootScope);
}).not.toThrow();
expect(nodeName_(element)).toMatch(/thead/i);
}));
it('should support templates with root <tbody> tags', inject(function($compile, $rootScope) {
expect(function() {
element = $compile('<div replace-with-tbody></div>')($rootScope);
}).not.toThrow();
expect(nodeName_(element)).toMatch(/tbody/i);
}));
it('should support templates with root <tfoot> tags', inject(function($compile, $rootScope) {
expect(function() {
element = $compile('<div replace-with-tfoot></div>')($rootScope);
}).not.toThrow();
expect(nodeName_(element)).toMatch(/tfoot/i);
}));
});
@@ -833,10 +855,18 @@ describe('$compile', function() {
replace: true,
templateUrl: 'th.html'
}));
directive('replaceWithThead', valueFn({
replace: true,
templateUrl: 'thead.html'
}));
directive('replaceWithTbody', valueFn({
replace: true,
templateUrl: 'tbody.html'
}));
directive('replaceWithTfoot', valueFn({
replace: true,
templateUrl: 'tfoot.html'
}));
}
));
@@ -1500,6 +1530,15 @@ describe('$compile', function() {
expect(nodeName_(element)).toMatch(/th/i);
}));
it('should support templates with root <thead> tags', inject(function($compile, $rootScope, $templateCache) {
$templateCache.put('thead.html', '<thead><tr><td>TD</td></tr></thead>');
expect(function() {
element = $compile('<div replace-with-thead></div>')($rootScope);
}).not.toThrow();
$rootScope.$digest();
expect(nodeName_(element)).toMatch(/thead/i);
}));
it('should support templates with root <tbody> tags', inject(function($compile, $rootScope, $templateCache) {
$templateCache.put('tbody.html', '<tbody><tr><td>TD</td></tr></tbody>');
expect(function() {
@@ -1508,6 +1547,15 @@ describe('$compile', function() {
$rootScope.$digest();
expect(nodeName_(element)).toMatch(/tbody/i);
}));
it('should support templates with root <tfoot> tags', inject(function($compile, $rootScope, $templateCache) {
$templateCache.put('tfoot.html', '<tfoot><tr><td>TD</td></tr></tfoot>');
expect(function() {
element = $compile('<div replace-with-tfoot></div>')($rootScope);
}).not.toThrow();
$rootScope.$digest();
expect(nodeName_(element)).toMatch(/tfoot/i);
}));
});
+21
View File
@@ -733,6 +733,27 @@ describe('select', function() {
expect(sortedHtml(options[2])).toEqual('<option value="1">3</option>');
});
it('should not update selected property of an option element on digest with no change event',
function() {
createSingleSelect();
scope.$apply(function() {
scope.values = [{name: 'A'}, {name: 'B'}, {name: 'C'}];
scope.selected = scope.values[0];
});
var options = element.find('option');
var optionToSelect = options.eq(1);
expect(optionToSelect.text()).toBe('B');
optionToSelect.prop('selected', true);
scope.$digest();
expect(optionToSelect.prop('selected')).toBe(true);
expect(scope.selected).toBe(scope.values[0]);
});
describe('binding', function() {
it('should bind to scope value', function() {
+12
View File
@@ -31,4 +31,16 @@ describe('Filter: orderBy', function() {
toEqual([{a:2, b:1},{a:15, b:1}]);
});
it('should support string predicates with names containing non-identifier characters', function() {
expect(orderBy([{"Tip %": .25}, {"Tip %": .15}, {"Tip %": .40}], '"Tip %"'))
.toEqualData([{"Tip %": .15}, {"Tip %": .25}, {"Tip %": .40}]);
expect(orderBy([{"원": 76000}, {"원": 31000}, {"원": 156000}], '"원"'))
.toEqualData([{"원": 31000}, {"원": 76000}, {"원": 156000}])
});
it('should throw if quoted string predicate is quoted incorrectly', function() {
expect(function() {
return orderBy([{"Tip %": .15}, {"Tip %": .25}, {"Tip %": .40}], '"Tip %\'');
}).toThrow();
});
});
+24 -6
View File
@@ -456,7 +456,17 @@ describe('$httpBackend', function() {
}
it('should convert 0 to 200 if content', function() {
it('should convert 0 to 200 if content and file protocol', function() {
$backend = createHttpBackend($browser, createMockXhr);
$backend('GET', 'file:///whatever/index.html', null, callback);
respond(0, 'SOME CONTENT');
expect(callback).toHaveBeenCalled();
expect(callback.mostRecentCall.args[0]).toBe(200);
});
it('should convert 0 to 200 if content for protocols other than file', function() {
$backend = createHttpBackend($browser, createMockXhr);
$backend('GET', 'someProtocol:///whatever/index.html', null, callback);
@@ -466,17 +476,25 @@ describe('$httpBackend', function() {
expect(callback.mostRecentCall.args[0]).toBe(200);
});
it('should convert 0 to 404 if no content', function() {
it('should convert 0 to 404 if no content and file protocol', function() {
$backend = createHttpBackend($browser, createMockXhr);
$backend('GET', 'someProtocol:///whatever/index.html', null, callback);
$backend('GET', 'file:///whatever/index.html', null, callback);
respond(0, '');
expect(callback).toHaveBeenCalled();
expect(callback.mostRecentCall.args[0]).toBe(404);
});
it('should not convert 0 to 404 if no content for protocols other than file', function() {
$backend = createHttpBackend($browser, createMockXhr);
$backend('GET', 'someProtocol:///whatever/index.html', null, callback);
respond(0, '');
expect(callback).toHaveBeenCalled();
expect(callback.mostRecentCall.args[0]).toBe(0);
});
it('should convert 0 to 404 if no content - relative url', function() {
var originalUrlParsingNode = urlParsingNode;
@@ -486,10 +504,10 @@ describe('$httpBackend', function() {
hash : "#/C:/",
host : "",
hostname : "",
href : "someProtocol:///C:/base#!/C:/foo",
href : "file:///C:/base#!/C:/foo",
pathname : "/C:/foo",
port : "",
protocol : "someProtocol:",
protocol : "file:",
search : "",
setAttribute: angular.noop
};
+10
View File
@@ -989,6 +989,16 @@ describe('$http', function() {
});
it('should ignore Blob objects', function () {
if (!window.Blob) return;
var blob = new Blob(['blob!'], { type: 'text/plain' });
$httpBackend.expect('POST', '/url', '[object Blob]').respond('');
$http({ method: 'POST', url: '/url', data: blob });
});
it('should have access to request headers', function() {
$httpBackend.expect('POST', '/url', 'header1').respond(200);
$http.post('/url', 'req', {
+54
View File
@@ -31,6 +31,35 @@ describe('$$rAF', function() {
expect(present).toBe(true);
}));
describe('$timeout fallback', function() {
it("it should use a $timeout incase native rAF isn't suppored", function() {
var timeoutSpy = jasmine.createSpy('callback');
//we need to create our own injector to work around the ngMock overrides
var injector = createInjector(['ng', function($provide) {
$provide.value('$timeout', timeoutSpy);
$provide.value('$window', {
location : window.location,
});
}]);
var $$rAF = injector.get('$$rAF');
expect($$rAF.supported).toBe(false);
var message;
$$rAF(function() {
message = 'on';
});
expect(message).toBeUndefined();
expect(timeoutSpy).toHaveBeenCalled();
timeoutSpy.mostRecentCall.args[0]();
expect(message).toBe('on');
});
});
describe('mocks', function() {
it('should throw an error if no frames are present', inject(function($$rAF) {
if($$rAF.supported) {
@@ -44,4 +73,29 @@ describe('$$rAF', function() {
}
}));
});
describe('mobile', function() {
it('should provide a cancellation method for an older version of Android', function() {
//we need to create our own injector to work around the ngMock overrides
var injector = createInjector(['ng', function($provide) {
$provide.value('$window', {
location : window.location,
webkitRequestAnimationFrame: jasmine.createSpy('$window.webkitRequestAnimationFrame'),
webkitCancelRequestAnimationFrame: jasmine.createSpy('$window.webkitCancelRequestAnimationFrame')
});
}]);
var $$rAF = injector.get('$$rAF');
var $window = injector.get('$window');
var cancel = $$rAF(function() {});
expect($$rAF.supported).toBe(true);
try {
cancel();
} catch(e) {}
expect($window.webkitCancelRequestAnimationFrame).toHaveBeenCalled();
});
});
});
+82 -38
View File
@@ -483,102 +483,131 @@ describe('Scope', function() {
describe('$watchCollection', function() {
var log, $rootScope, deregister;
beforeEach(inject(function(_$rootScope_) {
log = [];
beforeEach(inject(function(_$rootScope_, _log_) {
$rootScope = _$rootScope_;
deregister = $rootScope.$watchCollection('obj', function logger(obj) {
log.push(toJson(obj));
log = _log_;
deregister = $rootScope.$watchCollection('obj', function logger(newVal, oldVal) {
var msg = {newVal: newVal, oldVal: oldVal};
if (newVal === oldVal) {
msg.identical = true;
}
log(msg);
});
}));
it('should not trigger if nothing change', inject(function($rootScope) {
$rootScope.$digest();
expect(log).toEqual([undefined]);
expect(log).toEqual([{ newVal : undefined, oldVal : undefined, identical : true }]);
log.reset();
$rootScope.$digest();
expect(log).toEqual([undefined]);
expect(log).toEqual([]);
}));
it('should allow deregistration', inject(function($rootScope) {
it('should allow deregistration', function() {
$rootScope.obj = [];
$rootScope.$digest();
expect(log).toEqual(['[]']);
expect(log.toArray().length).toBe(1);
log.reset();
$rootScope.obj.push('a');
deregister();
$rootScope.$digest();
expect(log).toEqual(['[]']);
}));
expect(log).toEqual([]);
});
describe('array', function() {
it('should return oldCollection === newCollection only on the first listener call',
inject(function($rootScope, log) {
// first time should be identical
$rootScope.obj = ['a', 'b'];
$rootScope.$digest();
expect(log).toEqual([{newVal: ['a', 'b'], oldVal: ['a', 'b'], identical: true}]);
log.reset();
// second time should be different
$rootScope.obj[1] = 'c';
$rootScope.$digest();
expect(log).toEqual([{newVal: ['a', 'c'], oldVal: ['a', 'b']}]);
}));
it('should trigger when property changes into array', function() {
$rootScope.obj = 'test';
$rootScope.$digest();
expect(log).toEqual(['"test"']);
expect(log.empty()).toEqual([{newVal: "test", oldVal: "test", identical: true}]);
$rootScope.obj = [];
$rootScope.$digest();
expect(log).toEqual(['"test"', '[]']);
expect(log.empty()).toEqual([{newVal: [], oldVal: "test"}]);
$rootScope.obj = {};
$rootScope.$digest();
expect(log).toEqual(['"test"', '[]', '{}']);
expect(log.empty()).toEqual([{newVal: {}, oldVal: []}]);
$rootScope.obj = [];
$rootScope.$digest();
expect(log).toEqual(['"test"', '[]', '{}', '[]']);
expect(log.empty()).toEqual([{newVal: [], oldVal: {}}]);
$rootScope.obj = undefined;
$rootScope.$digest();
expect(log).toEqual(['"test"', '[]', '{}', '[]', undefined]);
expect(log.empty()).toEqual([{newVal: undefined, oldVal: []}]);
});
it('should not trigger change when object in collection changes', function() {
$rootScope.obj = [{}];
$rootScope.$digest();
expect(log).toEqual(['[{}]']);
expect(log.empty()).toEqual([{newVal: [{}], oldVal: [{}], identical: true}]);
$rootScope.obj[0].name = 'foo';
$rootScope.$digest();
expect(log).toEqual(['[{}]']);
expect(log).toEqual([]);
});
it('should watch array properties', function() {
$rootScope.obj = [];
$rootScope.$digest();
expect(log).toEqual(['[]']);
expect(log.empty()).toEqual([{newVal: [], oldVal: [], identical: true}]);
$rootScope.obj.push('a');
$rootScope.$digest();
expect(log).toEqual(['[]', '["a"]']);
expect(log.empty()).toEqual([{newVal: ['a'], oldVal: []}]);
$rootScope.obj[0] = 'b';
$rootScope.$digest();
expect(log).toEqual(['[]', '["a"]', '["b"]']);
expect(log.empty()).toEqual([{newVal: ['b'], oldVal: ['a']}]);
$rootScope.obj.push([]);
$rootScope.obj.push({});
log = [];
$rootScope.$digest();
expect(log).toEqual(['["b",[],{}]']);
expect(log.empty()).toEqual([{newVal: ['b', [], {}], oldVal: ['b']}]);
var temp = $rootScope.obj[1];
$rootScope.obj[1] = $rootScope.obj[2];
$rootScope.obj[2] = temp;
$rootScope.$digest();
expect(log).toEqual([ '["b",[],{}]', '["b",{},[]]' ]);
expect(log.empty()).toEqual([{newVal: ['b', {}, []], oldVal: ['b', [], {}]}]);
$rootScope.obj.shift();
log = [];
$rootScope.$digest();
expect(log).toEqual([ '[{},[]]' ]);
expect(log.empty()).toEqual([{newVal: [{}, []], oldVal: ['b', {}, []]}]);
});
it('should not infinitely digest when current value is NaN', function() {
$rootScope.obj = [NaN];
expect(function() {
$rootScope.$digest();
}).not.toThrow();
});
it('should watch array-like objects like arrays', function () {
@@ -601,57 +630,72 @@ describe('Scope', function() {
describe('object', function() {
it('should return oldCollection === newCollection only on the first listener call',
inject(function($rootScope, log) {
$rootScope.obj = {'a': 'b'};
// first time should be identical
$rootScope.$digest();
expect(log.empty()).toEqual([{newVal: {'a': 'b'}, oldVal: {'a': 'b'}, identical: true}]);
// second time not identical
$rootScope.obj.a = 'c';
$rootScope.$digest();
expect(log).toEqual([{newVal: {'a': 'c'}, oldVal: {'a': 'b'}}]);
}));
it('should trigger when property changes into object', function() {
$rootScope.obj = 'test';
$rootScope.$digest();
expect(log).toEqual(['"test"']);
expect(log.empty()).toEqual([{newVal: 'test', oldVal: 'test', identical: true}]);
$rootScope.obj = {};
$rootScope.$digest();
expect(log).toEqual(['"test"', '{}']);
expect(log.empty()).toEqual([{newVal: {}, oldVal: 'test'}]);
});
it('should not trigger change when object in collection changes', function() {
$rootScope.obj = {name: {}};
$rootScope.$digest();
expect(log).toEqual(['{"name":{}}']);
expect(log.empty()).toEqual([{newVal: {name: {}}, oldVal: {name: {}}, identical: true}]);
$rootScope.obj.name.bar = 'foo';
$rootScope.$digest();
expect(log).toEqual(['{"name":{}}']);
expect(log.empty()).toEqual([]);
});
it('should watch object properties', function() {
$rootScope.obj = {};
$rootScope.$digest();
expect(log).toEqual(['{}']);
expect(log.empty()).toEqual([{newVal: {}, oldVal: {}, identical: true}]);
$rootScope.obj.a= 'A';
$rootScope.$digest();
expect(log).toEqual(['{}', '{"a":"A"}']);
expect(log.empty()).toEqual([{newVal: {a: 'A'}, oldVal: {}}]);
$rootScope.obj.a = 'B';
$rootScope.$digest();
expect(log).toEqual(['{}', '{"a":"A"}', '{"a":"B"}']);
expect(log.empty()).toEqual([{newVal: {a: 'B'}, oldVal: {a: 'A'}}]);
$rootScope.obj.b = [];
$rootScope.obj.c = {};
log = [];
$rootScope.$digest();
expect(log).toEqual(['{"a":"B","b":[],"c":{}}']);
expect(log.empty()).toEqual([{newVal: {a: 'B', b: [], c: {}}, oldVal: {a: 'B'}}]);
var temp = $rootScope.obj.a;
$rootScope.obj.a = $rootScope.obj.b;
$rootScope.obj.c = temp;
$rootScope.$digest();
expect(log).toEqual([ '{"a":"B","b":[],"c":{}}', '{"a":[],"b":[],"c":"B"}' ]);
expect(log.empty()).
toEqual([{newVal: {a: [], b: {}, c: 'B'}, oldVal: {a: 'B', b: [], c: {}}}]);
delete $rootScope.obj.a;
log = [];
$rootScope.$digest();
expect(log).toEqual([ '{"b":[],"c":"B"}' ]);
expect(log.empty()).toEqual([{newVal: {b: {}, c: 'B'}, oldVal: {a: [], b: {}, c: 'B'}}]);
});
});
});
+1 -1
View File
@@ -109,7 +109,7 @@ describe('SCE', function() {
}));
it('should NOT wrap unknown contexts', inject(function($sce) {
expect(function() { $sce.trustAs('unknown1' , '123'); }).toThrowMinErr(
expect(function() { $sce.trustAs('unknown1', '123'); }).toThrowMinErr(
'$sce', 'icontext', 'Attempted to trust a value in invalid context. Context: unknown1; Value: 123');
}));
+43
View File
@@ -3356,6 +3356,49 @@ describe("ngAnimate", function() {
});
});
it('should animate only the specified CSS className inside ng-if', function() {
var captures = {};
module(function($animateProvider) {
$animateProvider.classNameFilter(/prefixed-animation/);
$animateProvider.register('.capture', function() {
return {
enter : buildFn('enter'),
leave : buildFn('leave')
};
function buildFn(key) {
return function(element, className, done) {
captures[key] = true;
(done || className)();
}
}
});
});
inject(function($rootScope, $compile, $rootElement, $document, $sniffer, $animate) {
if(!$sniffer.transitions) return;
var upperElement = $compile('<div><div ng-if=1><span class="capture prefixed-animation"></span></div></div>')($rootScope);
$rootElement.append(upperElement);
jqLite($document[0].body).append($rootElement);
$rootScope.$digest();
$animate.triggerCallbacks();
var element = upperElement.find('span');
var leaveDone = false;
$animate.leave(element, function() {
leaveDone = true;
});
$rootScope.$digest();
$animate.triggerCallbacks();
expect(captures['leave']).toBe(true);
expect(leaveDone).toBe(true);
});
});
it('should respect the most relevant CSS transition property if defined in multiple classes',
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
+14 -4
View File
@@ -45,15 +45,25 @@ describe('$cookies', function() {
}));
it('should drop or reset any cookie that was set to a non-string value',
it('should convert non-string values to string',
inject(function($cookies, $browser, $rootScope) {
$cookies.nonString = [1, 2, 3];
$cookies.nullVal = null;
$cookies.undefVal = undefined;
$cookies.preexisting = function() {};
var preexisting = $cookies.preexisting = function() {};
$rootScope.$digest();
expect($browser.cookies()).toEqual({'preexisting': 'oldCookie'});
expect($cookies).toEqual({'preexisting': 'oldCookie'});
expect($browser.cookies()).toEqual({
'preexisting': '' + preexisting,
'nonString': '1,2,3',
'nullVal': 'null',
'undefVal': 'undefined'
});
expect($cookies).toEqual({
'preexisting': '' + preexisting,
'nonString': '1,2,3',
'nullVal': 'null',
'undefVal': 'undefined'
});
}));
+9 -6
View File
@@ -52,16 +52,19 @@ describe('ngMock', function() {
it('should fake getHours method', function() {
//0 in -3h
var t0 = new angular.mock.TzDate(-3, 0);
// avoid going negative due to #5017, so use Jan 2, 1970 00:00 UTC
var jan2 = 24 * 60 * 60 * 1000;
//0:00 in -3h
var t0 = new angular.mock.TzDate(-3, jan2);
expect(t0.getHours()).toBe(3);
//0 in +0h
var t1 = new angular.mock.TzDate(0, 0);
//0:00 in +0h
var t1 = new angular.mock.TzDate(0, jan2);
expect(t1.getHours()).toBe(0);
//0 in +3h
var t2 = new angular.mock.TzDate(3, 0);
//0:00 in +3h
var t2 = new angular.mock.TzDate(3, jan2);
expect(t2.getHours()).toMatch(21);
});
+83 -23
View File
@@ -370,40 +370,100 @@ describe('ngClick (touch)', function() {
}));
it('should not cancel clicks that come long after', inject(function($rootScope, $compile) {
element1 = $compile('<div ng-click="count = count + 1"></div>')($rootScope);
describe('when clicking on a label immediately following a touch event', function() {
var touch = function(element, x, y) {
time = 10;
browserTrigger(element, 'touchstart',{
keys: [],
x: x,
y: y
});
$rootScope.count = 0;
time = 50;
browserTrigger(element, 'touchend',{
keys: [],
x: x,
y: y
});
};
$rootScope.$digest();
var click = function(element, x, y) {
browserTrigger(element, 'click',{
keys: [],
x: x,
y: y
});
};
expect($rootScope.count).toBe(0);
var $rootScope;
var container, otherElement, input, label;
beforeEach(inject(function(_$rootScope_, $compile, $rootElement) {
$rootScope = _$rootScope_;
var container = $compile('<div><div ng-click="count = count + 1"></div>' +
'<input id="input1" type="radio" ng-model="selection" value="radio1">' +
'<label for="input1">Input1</label></div>')($rootScope);
$rootElement.append(container);
otherElement = container.children()[0];
input = container.children()[1];
label = container.children()[2];
time = 10;
browserTrigger(element1, 'touchstart',{
keys: [],
x: 10,
y: 10
$rootScope.selection = 'initial';
$rootScope.$digest();
}));
afterEach(function() {
dealoc(label);
dealoc(input);
dealoc(otherElement);
dealoc(container);
});
time = 50;
browserTrigger(element1, 'touchend',{
keys: [],
x: 10,
y: 10
it('should not cancel input clicks with (0,0) coordinates', function() {
touch(otherElement, 100, 100);
time = 500;
click(label, 10, 10);
click(input, 0, 0);
expect($rootScope.selection).toBe('radio1');
});
expect($rootScope.count).toBe(1);
time = 2700;
browserTrigger(element1, 'click',{
keys: [],
x: 10,
y: 10
it('should not cancel input clicks with negative coordinates', function() {
touch(otherElement, 100, 100);
time = 500;
click(label, 10, 10);
click(input, -1, -1);
expect($rootScope.selection).toBe('radio1');
});
expect($rootScope.count).toBe(2);
}));
it('should not cancel input clicks with positive coordinates identical to label click', function() {
touch(otherElement, 100, 100);
time = 500;
click(label, 10, 10);
click(input, 10, 10);
expect($rootScope.selection).toBe('radio1');
});
it('should cancel input clicks with positive coordinates different than label click', function() {
touch(otherElement, 100, 100);
time = 500;
click(label, 10, 10);
click(input, 11, 11);
expect($rootScope.selection).toBe('initial');
});
});
});

Some files were not shown because too many files have changed in this diff Show More