Compare commits

...

94 commits

Author SHA1 Message Date
Nicolas Leger
c5f69e3f64 chore(ngdoc): fix misspellling of Naturally in sortVersionsNatrually
Corrects "sortVersionsNatrually" method name in `ngdoc.js` in "sortVersionsNaturally"
2014-02-04 16:43:39 -08:00
Daniel Luxemburg
dd24c78373 fix(ngMock): return false from mock $interval.cancel() when no argument is supplied
Closes #6103.
Closed #6099.
2014-02-04 16:41:25 -08:00
Caitlin Potter
36d37c0e38 fix(jqLite): trim HTML string in jqLite constructor
jQuery will construct DOM nodes containing leading whitespace. Prior to this change, jqLite would
throw a nosel minErr due to the first character of the string not being '<'. This change corrects
this behaviour by trimming the element string in jqLite constructor before testing for '<'.

Closes #6053
2014-02-04 16:39:52 -08:00
Caitlin Potter
24699ee8f0 fix($http): ignore xhr.responseType setter exception if value is "json"
WebKit added support for the json responseType value on 09/03/2013
https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are known to throw when
setting the value "json" as the response type. Other older browsers implementing the responseType.
Other browsers with infrequent update cycles may also be affected.

The json responseType value can be ignored if not supported, because JSON payloads are parsed on the
client-side regardless.

Closes #6115
Closes #6122
2014-02-04 19:34:31 -05:00
GiffenGood
aa6a0e3fc6 docs(log.js): param debugEnabled is a boolean and not a string 2014-02-04 16:26:36 -08:00
Tobias Bosch
8761ddc0e3 chore(release): be able to release any commit
The version information is now stored only in the tags.
By this we are able to release commits in the past, which
have already been tested, so we don't need a code freeze
or run tests any more. This is also the first step for
letting Travis do the releases in the future.

The package.json now contains the new
property 'branchVersion' that defines which tags are
valid on this branch.

Closes #6116
2014-02-04 16:20:22 -08:00
Caitlin Potter
058842ad04 revert: "fix($http): ignore xhr.responseType setter exception if value is "json""
This reverts commit 431bad0183.
2014-02-04 19:09:53 -05:00
Caitlin Potter
431bad0183 fix($http): ignore xhr.responseType setter exception if value is "json"
WebKit added support for the json responseType value on 09/03/2013
https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are known to throw when
setting the value "json" as the response type. Other older browsers implementing the responseType.
Other browsers with infrequent update cycles may also be affected.

The json responseType value can be ignored if not supported, because JSON payloads are parsed on the
client-side regardless.

Closes #6115
Closes #6122
2014-02-04 18:46:54 -05:00
Caitlin Potter
5850e61c82 docs(CHANGELOG): add breaking change notice for 1.2.9 $http default headers change
Add a mention for the slightly breaking change introduced in 1.2.9.

Closes #6022
2014-02-04 10:59:44 -05:00
Thomas Belin
d2e4e49986 fix(ngResource): don't filter "$"-prefixed properties from ngResource requests/responses
ngResource no longer filters properties prefixed with a single "$" character from requests or
responses, correcting a regression introduced in 1.2.6 (cb29632a) which caused shallowCopy and
shallowClearAndCopy to ignore properties prefixed with a single "$".

Closes #5666
Closes #6080
Closes #6033
2014-02-04 10:51:24 -05:00
Kamil Pekala
0da6cc9118 docs($compile): fixed syntax error.
"how to" was written twice in a row.

Closes #6110
2014-02-04 08:45:48 -05:00
John Kurlak
cc60ba1f35 docs($q): fixed grammatical error
"Promises" should be of the possessive form.

Closes #6082
2014-02-04 00:00:31 -08:00
Igor Minar
64d58a5b52 refactor(mocks): simplify the implementation 2014-02-03 22:41:18 -08:00
Wes Alvaro
3bf4390339 fix(mocks): always call functions injected with inject with this set to the current spec
Currently when a function is injected inside of a test we set the context to undefined which
is a bug.

Closes #6102
2014-02-03 22:25:30 -08:00
gdi2290
e7ac7aa43b chore(Gruntfile.js, package.json): use load-grunt-tasks and move grunt-contrib-jshint into devDependencies
Closes #6085
2014-02-03 19:25:47 -05:00
PatrickJS
37781ed145 style(License): update copyright year
Closes #6090
2014-02-03 19:19:29 -05:00
Igor Minar
19ba6510d0 chore(ngClass): remove debugger statement from an e2e test 2014-02-03 11:43:14 -08:00
jenkins
bc5ceee275 chore(release): update cdn version 2014-02-03 10:03:06 -08:00
jenkins
106af49258 chore(release): start v1.2.12 (1.2.12) 2014-02-03 09:41:17 -08:00
jenkins
d5eedf38db chore(release): cut v1.2.11 release 2014-02-03 09:40:03 -08:00
Tobias Bosch
c0c0b2b674 docs(changelog): release notes for 1.2.11 2014-02-03 09:28:45 -08:00
Caitlin Potter
ce1f1f97f0 fix(ngResource): don't append number to '$' in url param value when encoding URI
Previously, if a URL parameter value included a $, it would replace the dollar sign with a literal
'$1' for mysterious reasons. Using a function rather than a replacement string circumvents this
behaviour and produces a more expected result.

Closes #6003
Closes #6004
2014-02-03 11:12:07 -05:00
Alan Rubin
8205158e47 docs(animations): ngClass usage should link to ngClass documentation
Fixing reference to ngClass documentation

Closes #6089
2014-02-03 09:14:45 -05:00
Igor Minar
6609e3da76 fix(http): make jshint happy 2014-01-31 17:03:30 -08:00
Jorg
ef210e5e11 fix($http): update httpBackend to use ActiveXObject on IE8 if necessary
window.XMLHttpRequest is not always available in IE8 despite it not running in quirks mode,
in which case Angular should be using the ActiveXObject instead. Just checking the browser
version is taking too many shortcuts.

Closes #5677
Closes #5679
2014-01-31 16:52:42 -08:00
Stéphane Reynaud
fd61e222c3 docs(guide/templates.css-styling): fix "{{}}"
Replace {{}} to `{{}}` so that "{{}}" is rendered in the HTML page

Closes #5950
2014-01-31 16:31:06 -08:00
Lucas Galfasó
074b0675a1 fix($q): make $q.reject support finally and catch
Add support for the functions `finally` and `catch` to the
promise returned by `$q.reject`

Closes #6048
Closes #6076
2014-01-31 14:38:06 -05:00
Caitlin Potter
5ed721b9b5 fix($compile): retain CSS classes added in cloneAttachFn on asynchronous directives
Previously, classes added to asynchronous directive elements during the clone
attach function would not persist after the node is merged with the template, prior
to linking. This change corrects this behaviour and brings it in line with synchronous
directives.

Closes #5439
Closes #5617
2014-01-31 12:45:35 -05:00
Vojta Jina
c22ab5d2e2 chore(travis): remove BrowserStack credentials
Closes #5596
2014-01-31 07:48:36 -08:00
Igor Minar
339a1658cd fix(filterFilter): don't interpret dots in predicate object fields as paths
Closes #6005
Closes #6009
2014-01-31 03:16:46 -08:00
Shawn Flahave
29432ffe37 Edited the 'Flushing HTTP requests' section
Minor grammatical edits in the Flushing HTTP requests section.
2014-01-30 16:52:06 -08:00
Brad Williams
caed2dfe4f fix(docs): clarify doc for "args" in $broadcast and $emit
Closes #6047.
2014-01-30 16:50:08 -08:00
Igor Minar
7f4edaff6e chore(build): add jscs code style check to our build
Closes #6062
2014-01-30 11:39:53 -08:00
Igor Minar
6dcfccb32c chore(grunt): sort the grunt task load statements 2014-01-30 10:51:20 -08:00
Clark Pan
e2173f9101 docs($provide): return instance of Ping, rather than constructor
Updating $provide.service method docs

The previous example provided for the service method did not work.  I've updated the example to a working example.

I think this version of the example will probably make more sense to most people, and the factory method would be
a better place for this sort of example.

Closes #6008
2014-01-29 09:10:01 -05:00
Manan
7a78aed160 docs(compile/nodomevents): fix typo ng-mode -> ng-model
correcting directive name to ng-model instead of ng-mode in nodomevents.ngdoc

Correcting use of ng-model directive

Closes #6036
2014-01-29 08:51:02 -05:00
Julie
7aef2d54e0 test(docs): convert example end to end doc tests from scenario runner to protractor
Thanks to jeffbcross, petebacondarwin, btford, jdeboer, tbosch for contributions!

Closes #6023
2014-01-28 14:14:20 -05:00
Julie
ce37ae2868 docs(cookbook): remove the cookbook docs
The cookbook docs are now superceded by the guide. They are no longer available
in any menus and the only way to find them is to search for them. Remove!

Closes #5967
2014-01-28 14:12:52 -05:00
John Papa
95f0bf9b52 fix(mocks): refactor currentSpec to work w/ Jasmine 2
Closes #5662
2014-01-27 22:26:18 -08:00
Igor Minar
8a0be355a9 docs(triaging.md): update the 'PRs plz!' label name 2014-01-27 21:17:53 -08:00
Igor Minar
849a1489c7 style(triaging.md): removing ws 2014-01-27 12:06:17 -08:00
Brian Ford
f54f0f98a0 chore(docs): remove note about removing disqus 2014-01-27 11:46:18 -08:00
David Nelson
766b3d5c87 docs(select): rephase note on ngOptions
added 'an' and 'the' to ngOptions sentence

Closes #5993
2014-01-26 15:45:04 -05:00
David Nelson
4f735b0605 docs(ngSwitch): fixed typo 'choses' to 'chooses'
Closes #5992
2014-01-26 15:42:27 -05:00
jenkins
319dd1a449 chore(release): update cdn version 2014-01-24 19:27:18 -08:00
jenkins
88a14b4e25 chore(release): start v1.2.11 (1.2.11) 2014-01-24 15:42:49 -08:00
jenkins
451bde1244 chore(release): cut v1.2.10 release 2014-01-24 15:28:28 -08:00
Matias Niemelä
4f827f587b docs(changelog): release notes for 1.2.10 2014-01-24 13:34:45 -05:00
Matias Niemelä
131410b61b docs(search): make sure the forward slash doesn't focus on search while on another input element
Closes #5969
2014-01-24 13:02:50 -05:00
Matias Niemelä
ca6b7d0fa2 feat($animate): provide support for a close callback
Closes #5685
Closes #5053
Closes #4993
2014-01-24 12:21:29 -05:00
Brian Ford
40dc806e03 docs(guide/filter): link to an example of a filter unit test 2014-01-23 13:43:31 -08:00
Peter Bacon Darwin
5e9835b4f2 docs(ngdoc): ensure module installation docs are accurate
The ngMock module is built into a package called angular-mocks, which is
not named consistently and the docs were giving invalid info.

Closes #5810
2014-01-23 17:27:30 +00:00
Caitlin Potter
e0209169bf fix(a): don't preventDefault on click when SVGAElement has an xlink:href attribute
Before this change, an SVGAElement with an xlink:href attribute and no href or name attribute which
was compiled by the angular HTML compiler would never be clickable, due to the htmlAnchorDirective
calling event.preventDefault() due to the missing href attribute.

This change corrects this behaviour by also testing the xlink:href attribute if the element in
question is determined to be an SVG anchor tag (with the href property having type SVGAnimatedString)

Closes #5896
Closes #5897
2014-01-22 14:51:47 -05:00
Martin Probst
b07afa0465 refactor(externs): move Closure Externs back to Closure code repository
While Closure Compiler generally recommends to maintain the externs for
projects together with their source, this has not worked well for
AngularJS:
- Changes to externs must be tested; they can break clients. AngularJS
  has no testing infrastructure for this.
- Changes mostly come from users inside of Google and are much more
  easily submitted together with the code using them within Google's
  repository.

This change deletes the externs here and adds a README.closure.md to
document the change. They will be added back to Closure Compiler in a
separate submit.

Closes #5906
2014-01-22 11:38:14 -08:00
Allon Hadaya
c3b5e16d84 docs(ngRoute): grammar correction
grammar: occurs -> occur

Closes #5937
2014-01-22 14:26:33 -05:00
Igor Minar
7f444a205e style($interval): remove ws and replace comma with semicolon 2014-01-22 11:14:59 -08:00
Julie
82213efff2 test(doc:protractor): turn off animation for doc end to end tests to speed things up 2014-01-22 12:41:35 -05:00
gabrielbrasil
ec59be67bc docs(ngMock): $log.error property contains messages from $log.error, not $log.log
Closes #5932
2014-01-22 11:34:56 -05:00
Caitlin Potter
79e519feda fix(input): use Chromium's email validation regexp
This change uses the regexp from Chromium/Blink to validate emails, and corrects
an error in the validation engine, which previously considered an invalid email
to be valid. Additionally, the regexp was invalidating emails with capital
letters, however this is not the behaviour recomended in the spec, or implemented
in Chromium.

Closes #5899
Closes #5924
2014-01-21 21:40:55 -05:00
matthewhintzen
7cf5544a9f docs(tutorial): update step_12.ngdoc
This time I feel good about this modification to the document, the code listing
on the tutorial page for the animation.js DID NOT match what was actually IN the
file for that branch.  Updated tutorial to reflect actual contents of file

Closes #5922
2014-01-21 17:23:05 -08:00
Narretz
030a9b8d33 docs(changelog): remove reverted commit from 1.2.9
Closes #5868
2014-01-21 17:17:14 -08:00
Igor Minar
310f129c1d docs(guide/directive): clarify attr object definition
Closes #5884
2014-01-21 17:14:00 -08:00
Igor Minar
0fc64ad8a1 style(guide/directive): remove ws 2014-01-21 17:12:34 -08:00
Jan Hancic
12d1f5700d chore(docs): add spacing to tutorial buttons
Added 5px of right margin to tutorial buttons (Previous, Live demom ...).

Closes #5901
2014-01-21 17:04:49 -08:00
Brian Ford
60e80509a8 docs(select): add callout style for a note 2014-01-21 17:02:50 -08:00
Dan Matthews
3c12d36e73 docs(select): add object comparison warning 2014-01-21 16:58:41 -08:00
Caitlin Potter
fd6bac7de5 fix(ngRoute): pipe preceding route param no longer masks ? or * operator
Before this change,

```js
$routeProvider.when('/foo/:bar|?', { ... });
```

would not have the expected effect --- the parameter would not be optional, and
the pipe would not be included in the parameter name.

Following this change, the presence of the pipe operator will typically cause an
exception to be thrown due to the fact that the generated regexp is invalid.

The net result of this change is that ? and * operators will not be masked, and
pipe operators will need to be removed, although it's unexpected that these are
being used anywhere.

Closes #5920
2014-01-21 19:56:57 -05:00
George Cox
6d525f06c0 docs(tutorial): remove 'going forward' nonsense
Closes #5914

time/space dimension error
2014-01-21 16:45:27 -08:00
George Cox
d7e6f1b192 docs(tutorial): fix grammar
Closes #5909
2014-01-21 16:43:52 -08:00
Stéphane Reynaud
5b9ff6cf48 docs(guide/controller): fix duplicated "and" typo
Remove the second instance of the word "and" from the docs where it was duplicated.
2014-01-21 16:28:30 -08:00
Lucas Galfasó
f09b6aa5b5 fix($parse): do not use locals to resolve object properties
Do not use the locals when performing a field access in an angular expression.

Closes #5838
Closes #5862
2014-01-21 19:27:04 -05:00
Abraham
8b395ff325 docs(ngEventDirs): document priority of event directives
The general assumption is that if @priority is not defined, the priority is 0. BUT it's not
necessarily harmful to be explicit about this.

Closes #5852
2014-01-19 23:29:39 -05:00
Christoph Burgdorf
6c9131ef10 docs($http): remove outdated part about $http outside of $apply phase
This removes some outdated advice which no longer is true against the latest angular version.

The information about unit testing with ngMocks remains, because it's always good to have
information like that easily found. This little snippet is not worded perfectly, and is not
a very good example unit test, so additional work is needed here.

Relates to #5206
Closes #5485
2014-01-18 22:52:58 -05:00
Vlad GURDIGA
99c5027bf2 docs($provide): fix Markdown formatting for provider method
Indentation made Markdown parser think that it’s a block of code.

Closes #5446
2014-01-18 22:09:56 -05:00
letsmakesense
90e60d2d54 docs($q): note that function okToGreet is expected to be defined in example
the function okToGreet wasn't defined, so this example wouldn't work properly.

I've decided that instead of adding unrelated code to the example, it should just be noted that the
function is expected to be defined in the lexical scope.

Closes #5878
2014-01-18 17:36:58 -05:00
Bastian Buchholz
928d000db7 docs(ngInit): ng-init is an attribute/class directive, not an element directive
As discussed in comments on 42ec95ebae (commitcomment-5109829),
ngInit is not an element directive, so @clkao's example should reflect this.

Closes #5879
2014-01-18 17:10:46 -05:00
Chia-liang Kao
42ec95ebae docs(ngInit): note precedence when used with filters
Clears up expression priority issue

Closes #3869
Closes #5873
2014-01-18 15:35:33 -05:00
Joshua Flanagan
1028cfaa30 docs(services): use $log service in example consistently with $log API
The $log provider returns an object and not a function, so this example, which appears to be using
the $log provider, should call it as it would be called in a real-world application.

Closes #5875
2014-01-18 15:26:18 -05:00
Ben McCann
756c52d6c1 docs(directive): link to directive registration api function
Originally, this issue was regarding documenting `restrict: 'CM'` in the directive guide, but it
was pointed out that the restrict documentation is covered in the $compile documentation. Because of
this, a link was simply added to the $compile documentation.

However, the wording suggests that it's actually linking to the directive registration function, in
$compileProvider, so the docs will link there instead. There is a link only a paragraph below to the
$compile documentation, so this does not hurt.

Closes #5516
2014-01-17 22:36:05 -05:00
Jeff Jewiss
c3e1a41d6f docs(api): adds links to top level guides
The main api docs page is probably the main landing page for many devs
looking to learn angular, so linking to the main guide pages would
likely help.

Closes #5869
2014-01-17 19:19:27 -05:00
Abdessamad Idrissi
8dd4f14a04 docs(input): document ngValue directive
Extend the example with ng-value showing how to deal with default checked radio boxes.

Closes #5654
2014-01-17 18:15:25 -05:00
Cameron Spear
7ba30fd2e7 docs($sce): correct documentation for angular 1.2.0
Include mention of `ngSanitize` (and add it to the example), as well as removing (and clarifying if
needed) references to `ng-html-bind-unsafe`.

Closes #5551
2014-01-17 17:12:53 -05:00
Andreas Gruenbacher
5adea0ba64 docs(ngChange): clarify difference between ng-change DOM change event
The ng-change event triggers immediately, which makes a difference for text input fields and text
areas, where the JavaScript onchange event would only be called at the end of the change.

Closes #5640
2014-01-17 16:40:10 -05:00
Bruce Davidson
2262ca6697 docs(contributing): add instructions to install grunt-cli/bower globally on unix systems
Closes #5814
Closes #5811
2014-01-17 15:51:29 -05:00
刘朋飞
cd74f74468 docs(tutorial): add missing semicolon to code snippet
Lets encourage people to use semicolons in javascript :>

Closes #5834
2014-01-17 15:34:33 -05:00
Neil Rubens
fced1c0c16 docs(tutorial): fix typo
Correction misspelling: easist -> easiest

Closes #5850
2014-01-16 23:11:52 -05:00
Rob Wormald
1cdcddb5cc docs(animations): renamed animate-show-hide to sample-show-hide
This always throws me off - I think it helps to make it clear that the class name is arbitrary, and
what matters is the .ng-etc classes.

Closes #5848
2014-01-16 21:49:01 -05:00
Leniel Macaferi
0e4d7cacad docs(tutorial): reference node command before scripts\web-server.js
Amended to also clarify this note in the mac/linux tab.

Closes #5845
2014-01-16 19:13:53 -05:00
akerekes
6a9ccacd62 docs(concepts): fix typos in explanation
Code uses module names with '2' as suffix while the explanation used the module names without the
suffix. The diagram is correct but also does not suffix the module names.

Closes #5567
2014-01-15 19:31:06 -05:00
Foxandxss
e591ddcb30 docs(ngShowHide): make a note of values considered to be falsy
This issue has been a focus of problems for some users and we discussed it on the IRC that it should
be at least documented.

~Amended the style to use bootstrap notes, I think overall it looks better and catches the eyes more
easily. However there are no anchor links to these, if these are necessary they can be added later.

Closes #3436
Closes #5762
2014-01-15 17:59:01 -05:00
John Lannon
cd0af8a771 docs(README): fix typo
Closes #5813
2014-01-15 17:46:12 -05:00
Roy Ling
51a7f9dc4a docs(guide/directive): rephrase for consistency
- referring to `=attr` rather than `=prop` is consistent with note under example with =customerInfo
- change `prop` to `attr` (basically `prop` refers to property in JS object, `attr` is for HTML tag)
- change the function name in description to match the name in code example

Closes #5786
2014-01-15 17:41:17 -05:00
jenkins
61eb426ab4 chore(release): update cdn version 2014-01-15 13:39:34 -08:00
jenkins
8ea8da4f11 chore(release): start v1.2.10 (1.2.10) 2014-01-15 10:15:55 -08:00
109 changed files with 1562 additions and 3614 deletions

3
.jscs.json Normal file
View file

@ -0,0 +1,3 @@
{
"disallowKeywords": ["with"]
}

22
.jscs.json.todo Normal file
View file

@ -0,0 +1,22 @@
// This is an incomplete TODO list of checks we want to start enforcing
//
// The goal is to enable these checks one by one by moving them to .jscs.json along with commits
// that correct the existing code base issues and make the new check pass.
{
"requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"],
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
"disallowLeftStickedOperators": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
"disallowRightStickedOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
"requireRightStickedOperators": ["!"],
"requireLeftStickedOperators": [","],
"disallowImplicitTypeConversion": ["string"],
"disallowMultipleLineBreaks": true,
"disallowKeywordsOnNewLine": ["else"],
"disallowTrailingWhitespace": true,
"requireLineFeedAtFileEnd": true,
"validateJSDoc": {
"checkParamNames": true,
"requireParamTypes": true
}
}

View file

@ -9,8 +9,6 @@ env:
global:
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
- BROWSER_STACK_USERNAME=VojtaJina
- BROWSER_STACK_ACCESS_KEY=HAfHZaypxAc3PEUrUU9a
- LOGS_DIR=/tmp/angular-build/logs
- BROWSER_PROVIDER_READY_FILE=/tmp/sauce-connect-ready

View file

@ -1,3 +1,54 @@
<a name="1.2.11"></a>
# 1.2.11 cryptocurrency-hyperdeflation (2014-02-03)
## Bug Fixes
- **$compile:** retain CSS classes added in cloneAttachFn on asynchronous directives
([5ed721b9](https://github.com/angular/angular.js/commit/5ed721b9b5e95ae08450e1ae9d5202e7f3f79295),
[#5439](https://github.com/angular/angular.js/issues/5439), [#5617](https://github.com/angular/angular.js/issues/5617))
- **$http:** update httpBackend to use ActiveXObject on IE8 if necessary
([ef210e5e](https://github.com/angular/angular.js/commit/ef210e5e119db4f5bfc9d2428b19f9b335c4f976),
[#5677](https://github.com/angular/angular.js/issues/5677), [#5679](https://github.com/angular/angular.js/issues/5679))
- **$q:** make $q.reject support `finally` and `catch`
([074b0675](https://github.com/angular/angular.js/commit/074b0675a1f97dce07f520f1ae6198ed3c604000),
[#6048](https://github.com/angular/angular.js/issues/6048), [#6076](https://github.com/angular/angular.js/issues/6076))
- **filterFilter:** don't interpret dots in predicate object fields as paths
([339a1658](https://github.com/angular/angular.js/commit/339a1658cd9bfa5e322a01c45aa0a1df67e3a842),
[#6005](https://github.com/angular/angular.js/issues/6005), [#6009](https://github.com/angular/angular.js/issues/6009))
- **mocks:** refactor currentSpec to work w/ Jasmine 2
([95f0bf9b](https://github.com/angular/angular.js/commit/95f0bf9b526fda8964527c6d4aef1ad50a47f1f3),
[#5662](https://github.com/angular/angular.js/issues/5662))
- **ngResource:** don't append number to '$' in url param value when encoding URI
([ce1f1f97](https://github.com/angular/angular.js/commit/ce1f1f97f0ebf77941b2bdaf5e8352d33786524d),
[#6003](https://github.com/angular/angular.js/issues/6003), [#6004](https://github.com/angular/angular.js/issues/6004))
<a name="1.2.10"></a>
# 1.2.10 augmented-serendipity (2014-01-24)
## Bug Fixes
- **$parse:** do not use locals to resolve object properties
([f09b6aa5](https://github.com/angular/angular.js/commit/f09b6aa5b58c090e3b8f8811fb7735e38d4b7623),
[#5838](https://github.com/angular/angular.js/issues/5838), [#5862](https://github.com/angular/angular.js/issues/5862))
- **a:** don't call preventDefault on click when a SVGAElement has an xlink:href attribute
([e0209169](https://github.com/angular/angular.js/commit/e0209169bf1463465ad07484421620748a4d3908),
[#5896](https://github.com/angular/angular.js/issues/5896), [#5897](https://github.com/angular/angular.js/issues/5897))
- **input:** use Chromium's email validation regexp
([79e519fe](https://github.com/angular/angular.js/commit/79e519fedaec54390a8bdacfb1926bfce57a1eb6),
[#5899](https://github.com/angular/angular.js/issues/5899), [#5924](https://github.com/angular/angular.js/issues/5924))
- **ngRoute:** pipe preceding route param no longer masks ? or * operator
([fd6bac7d](https://github.com/angular/angular.js/commit/fd6bac7de56f728a89782dc80c78f7d5c21bbc65),
[#5920](https://github.com/angular/angular.js/issues/5920))
## Features
- **$animate:** provide support for a close callback
([ca6b7d0f](https://github.com/angular/angular.js/commit/ca6b7d0fa2e355ebd764230260758cee9a4ebe1e),
[#5685](https://github.com/angular/angular.js/issues/5685), [#5053](https://github.com/angular/angular.js/issues/5053), [#4993](https://github.com/angular/angular.js/issues/4993))
<a name="1.2.9"></a>
# 1.2.9 enchanted-articulacy (2014-01-15)
@ -20,9 +71,6 @@
- **$rootScope:** prevent infinite $digest by checking if asyncQueue is empty when decrementing ttl
([2cd09c9f](https://github.com/angular/angular.js/commit/2cd09c9f0e7766bcd191662841b7b1ffc3b6dc3f),
[#2622](https://github.com/angular/angular.js/issues/2622))
- **$route:** update current route upon $route instantiation
([2b344dbd](https://github.com/angular/angular.js/commit/2b344dbd20777fb1283b3a5bcf35a6ae8d09469d),
[#4957](https://github.com/angular/angular.js/issues/4957))
## Features
@ -34,6 +82,30 @@
([4ae3184c](https://github.com/angular/angular.js/commit/4ae3184c5915aac9aa00889aa2153c8e84c14966),
[#4278](https://github.com/angular/angular.js/issues/4278), [#4225](https://github.com/angular/angular.js/issues/4225))
## Breaking Changes
- **$http:** due to [e1cfb195](https://github.com/angular/angular.js/commit/e1cfb1957feaf89408bccf48fae6f529e57a82fe),
it is now necessary to seperately 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:
Before:
// Will apply to POST, PUT and PATCH methods
$httpProvider.defaults.headers.post = {
"X-MY-CSRF-HEADER": "..."
};
After:
// POST, PUT and PATCH default headers must be specified seperately,
// 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)

View file

@ -4,17 +4,8 @@ var path = require('path');
module.exports = function(grunt) {
//grunt plugins
grunt.loadNpmTasks('grunt-bump');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-compress');
grunt.loadNpmTasks('grunt-jasmine-node');
grunt.loadNpmTasks('grunt-ddescribe-iit');
grunt.loadNpmTasks('grunt-merge-conflict');
grunt.loadNpmTasks('grunt-parallel');
grunt.loadNpmTasks('grunt-shell');
grunt.loadNpmTasks('grunt-contrib-jshint');
require('load-grunt-tasks')(grunt);
grunt.loadTasks('lib/grunt');
var NG_VERSION = util.getVersion();
@ -87,9 +78,7 @@ module.exports = function(grunt) {
jqlite: 'karma-jqlite.conf.js',
jquery: 'karma-jquery.conf.js',
docs: 'karma-docs.conf.js',
modules: 'karma-modules.conf.js',
//NOTE run grunt test:e2e instead and it will start a webserver for you
end2end: 'karma-e2e.conf.js'
modules: 'karma-modules.conf.js'
},
@ -147,6 +136,13 @@ module.exports = function(grunt) {
}
},
jscs: {
src: ['src/**/*.js', 'test/**/*.js'],
options: {
config: ".jscs.json"
}
},
build: {
scenario: {
dest: 'build/angular-scenario.js',
@ -288,15 +284,14 @@ module.exports = function(grunt) {
//alias tasks
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:e2e', 'webdriver', 'runprotractor:normal']);
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:protractor']);
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
grunt.registerTask('test:jquery', 'Run the jQuery unit tests with Karma', ['tests:jquery']);
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['tests:modules']);
grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']);
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['tests:jqlite', 'tests:jquery', 'tests:modules']);
grunt.registerTask('test:e2e', 'Run the end to end tests with Karma and keep a test server running in the background', ['connect:testserver', 'tests:end2end']);
// This should eventually replace test:e2e
grunt.registerTask('test:protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'runprotractor:normal']);
grunt.registerTask('test:e2e', 'Alias for test:protractor', ['test:protractor']);
grunt.registerTask('test:docgen', ['jasmine_node']);
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
@ -304,6 +299,6 @@ module.exports = function(grunt) {
grunt.registerTask('webserver', ['connect:devserver']);
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('package-without-bower', ['clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint']);
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'jscs']);
grunt.registerTask('default', ['package']);
};

View file

@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

23
README.closure.md Normal file
View file

@ -0,0 +1,23 @@
Using AngularJS with the Closure Compiler
=========================================
The Closure Compiler project contains externs definitions for AngularJS
JavaScript in its `contrib/externs` directory.
The definitions contain externs for use with the Closure compiler (aka
JSCompiler). Passing these files to the --externs parameter of a compiler
pass allows using type annotations for AngularJS objects. For example,
Angular's $scope objects can be annotated as:
```js
/** @type {angular.Scope} */
var scope = $scope;
```
This allows JSCompiler to type check accesses to scope, give warnings about
missing methods or incorrect arguments, and also prevents renaming of property
accesses with advanced compilation.
The externs are incomplete and maintained on an as-needed basis, but strive to
be correct. Externs for individual modules should be added in separate files.
See https://developers.google.com/closure/compiler/

View file

@ -7,7 +7,7 @@ syntax to express your applications components clearly and succinctly. It au
synchronizes data from your UI (view) with your JavaScript objects (model) through 2-way data
binding. To help you structure your application better and make it easy to test, AngularJS teaches
the browser how to do dependency injection and inversion of control. Oh yeah and it also helps with
server-side communication, taming async callbacks with promises and deferreds; and make client-side
server-side communication, taming async callbacks with promises and deferreds; and makes client-side
navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all:
it makes development fun!

View file

@ -27,7 +27,7 @@ The following is done automatically and should not be done manually:
* 1.2.x - everything else
1. Label "GH: *" (to be automated via Mary Poppins)
* PR - issue is a PR
* issue - otherwise
* issue - otherwise
1. Bugs:
* Label "Type: Bug"
* Label "Type: Regression" - if the bug is a regression
@ -43,7 +43,7 @@ The following is done automatically and should not be done manually:
* Goals of angular core? - Often new features should be implemented as a third-party module rather than an addition to the core.
1. Label "component: *"
* In rare cases, it's ok to have multiple components.
* In rare cases, it's ok to have multiple components.
1. Label "impact: *"
* small - obscure issue affecting one or handful of developers
* medium - impacts some usage patterns
@ -52,7 +52,7 @@ The following is done automatically and should not be done manually:
* small - trivial change
* medium - non-trivial but straightforward change
* large - changes to many components in angular or any changes to $compile, ngRepeat or other "fun" components
1. Label "PRs welcome" for "GH: issue"
1. Label "PRs plz!" for "GH: issue"
* if complexity is small or medium and the problem as well as solution are well captured in the issue
1. Label "origin: google" for issues from Google
1. Label "high priority" for security issues, major performance regressions or memory leaks

View file

@ -1,17 +0,0 @@
This file contains externs for use with the Closure compiler (aka JSCompiler).
Passing these files to the --externs parameter of a compiler pass allows using
type annotations for AngularJS objects. For example, Angular's $scope objects
can be annotated as:
```js
/** @type {angular.Scope} */
var scope = $scope;
```
This allows JSCompiler to type check accesses to scope, give warnings about
missing methods or incorrect arguments, and also prevents renaming of property
accesses with advanced compilation.
The externs are incomplete and maintained on an as-needed basis, but strive to
be correct. Externs for individual modules should be added in separate files.
See https://developers.google.com/closure/compiler/

1975
closure/angular.js vendored

File diff suppressed because it is too large Load diff

View file

@ -13,8 +13,7 @@ describe("docsSearch", function() {
results[0] = { section: 'tutorial', shortName: 'item one', keywords: 'item, one, 1' };
results[1] = { section: 'tutorial', shortName: 'item man', keywords: 'item, man' };
results[2] = { section: 'api', shortName: 'item other', keywords: 'item, other' };
results[3] = { section: 'cookbook', shortName: 'item cookbook', keywords: 'item, other' };
results[4] = { section: 'api', shortName: 'ngRepeat', keywords: 'item, other' };
results[3] = { section: 'api', shortName: 'ngRepeat', keywords: 'item, other' };
$provide.value('NG_PAGES', results);
$provide.factory('lunrSearch', function() {
@ -41,19 +40,14 @@ describe("docsSearch", function() {
expect(items['api'].length).toBe(2);
}));
it("should place cookbook items in the tutorial", inject(function(docsSearch) {
var items = docsSearch('item');
expect(items['tutorial'].length).toBe(3);
}));
it("should return all results without a search", inject(function(docsSearch) {
var items = docsSearch();
expect(items['tutorial'].length).toBe(3);
expect(items['tutorial'].length).toBe(2);
expect(items['api'].length).toBe(2);
}));
it("should store values with and without a ng prefix", inject(function(docsSearch) {
expect(interceptedLunrResults[4].title).toBe('ngRepeat repeat');
expect(interceptedLunrResults[3].title).toBe('ngRepeat repeat');
}));
});

View file

@ -5,8 +5,8 @@
# AngularJS API Docs
Welcome to the AngularJS API docs page. These pages contain the AngularJS reference materials for version <strong ng-bind="version"></strong>.
The documentation is organized into **modules** which contain various components of an AngularJS application.
These components are directives, services, filters, providers, types, global APIs and testing mocks.
The documentation is organized into **{@link guide/module modules}** which contain various components of an AngularJS application.
These components are {@link guide/directive directives}, {@link guide/dev_guide.services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates types}, global APIs and testing mocks.
<div class="alert alert-info">
**Angular Namespaces `$` and `$$`**

View file

@ -1,122 +0,0 @@
@ngdoc overview
@name Cookbook: Advanced Form
@description
Here we extend the basic form example to include common features such as reverting, dirty state
detection, and preventing invalid form submission.
<doc:example>
<doc:source>
<script>
function UserForm($scope) {
var master = {
name: 'John Smith',
address:{
line1: '123 Main St.',
city:'Anytown',
state:'AA',
zip:'12345'
},
contacts:[
{type:'phone', value:'1(234) 555-1212'}
]
};
$scope.state = /^\w\w$/;
$scope.zip = /^\d\d\d\d\d$/;
$scope.cancel = function() {
$scope.form = angular.copy(master);
};
$scope.save = function() {
master = $scope.form;
$scope.cancel();
};
$scope.addContact = function() {
$scope.form.contacts.push({type:'', value:''});
};
$scope.removeContact = function(index) {
$scope.form.contacts.splice(index, 1);
};
$scope.isCancelDisabled = function() {
return angular.equals(master, $scope.form);
};
$scope.isSaveDisabled = function() {
return $scope.myForm.$invalid || angular.equals(master, $scope.form);
};
$scope.cancel();
}
</script>
<div ng-controller="UserForm">
<form name="myForm">
<label>Name:</label><br/>
<input type="text" ng-model="form.name" required/> <br/><br/>
<label>Address:</label> <br/>
<input type="text" ng-model="form.address.line1" size="33" required/> <br/>
<input type="text" ng-model="form.address.city" size="12" required/>,
<input type="text" ng-model="form.address.state" size="2"
ng-pattern="state" required/>
<input type="text" ng-model="form.address.zip" size="5"
ng-pattern="zip" required/><br/><br/>
<label>Contacts:</label>
[ <a href="" ng-click="addContact()">add</a> ]
<div ng-repeat="contact in form.contacts">
<select ng-model="contact.type">
<option>email</option>
<option>phone</option>
<option>pager</option>
<option>IM</option>
</select>
<input type="text" ng-model="contact.value" required/>
[ <a href="" ng-click="removeContact($index)">X</a> ]
</div>
<button ng-click="cancel()" ng-disabled="isCancelDisabled()">Cancel</button>
<button ng-click="save()" ng-disabled="isSaveDisabled()">Save</button>
</form>
<hr/>
Debug View:
<pre>form={{form}}</pre>
</div>
</doc:source>
<doc:scenario>
it('should enable save button', function() {
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
input('form.name').enter('');
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
input('form.name').enter('change');
expect(element(':button:contains(Save)').attr('disabled')).toBeFalsy();
element(':button:contains(Save)').click();
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
});
it('should enable cancel button', function() {
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
input('form.name').enter('change');
expect(element(':button:contains(Cancel)').attr('disabled')).toBeFalsy();
element(':button:contains(Cancel)').click();
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
expect(element(':input[ng\\:model="form.name"]').val()).toEqual('John Smith');
});
</doc:scenario>
</doc:example>
#Things to notice
* Cancel & save buttons are only enabled if the form is dirty — there is something to cancel or
save.
* Save button is only enabled if there are no validation errors on the form.
* Cancel reverts the form changes back to original state.
* Save updates the internal model of the form.
* Debug view shows the two models. One presented to the user form and the other being the pristine
copy master.

View file

@ -1,63 +0,0 @@
@ngdoc overview
@name Cookbook: Resources - Buzz
@description
External resources are URLs that provide JSON data, which are then rendered with the help of
templates. Angular has a resource factory that can be used to give names to the URLs and then
attach behavior to them. For example you can use the
{@link http://code.google.com/apis/buzz/v1/getting_started.html#background-operations| Google Buzz
API}
to retrieve Buzz activity and comments.
<doc:example>
<doc:source>
<script>
BuzzController.$inject = ['$scope', '$resource'];
function BuzzController($scope, $resource) {
$scope.userId = 'googlebuzz';
$scope.Activity = $resource(
'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments',
{alt: 'json', callback: 'JSON_CALLBACK'},
{ get: {method: 'JSONP', params: {visibility: '@self'}},
replies: {method: 'JSONP', params: {visibility: '@self', comments: '@comments'}}
});
$scope.fetch = function() {
$scope.activities = $scope.Activity.get({userId:this.userId});
}
$scope.expandReplies = function(activity) {
activity.replies = $scope.Activity.replies({userId: this.userId, activityId: activity.id});
}
};
</script>
<div ng-controller="BuzzController">
<input ng-model="userId"/>
<button ng-click="fetch()">fetch</button>
<hr/>
<div class="buzz" ng-repeat="item in activities.data.items">
<h1 style="font-size: 15px;">
<img ng-src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
<a ng-href="{{item.actor.profileUrl}}">{{item.actor.name}}</a>
<a href ng-click="expandReplies(item)" style="float: right;">
Expand replies: {{item.links.replies[0].count}}
</a>
</h1>
{{item.object.content | html}}
<div class="reply" ng-repeat="reply in item.replies.data.items" style="margin-left: 20px;">
<img ng-src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
<a ng-href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>:
{{reply.content | html}}
</div>
</div>
</div>
</doc:source>
<doc:scenario>
xit('fetch buzz and expand', function() {
element(':button:contains(fetch)').click();
expect(repeater('div.buzz').count()).toBeGreaterThan(0);
element('.buzz a:contains(Expand replies):first').click();
expect(repeater('div.reply').count()).toBeGreaterThan(0);
});
</doc:scenario>
</doc:example>

View file

@ -1,151 +0,0 @@
@ngdoc overview
@name Cookbook: Deep Linking
@description
Deep linking allows you to encode the state of the application in the URL so that it can be
bookmarked and the application can be restored from the URL to the same state.
While Angular does not force you to deal with bookmarks in any particular way, it has services
which make the common case described here very easy to implement.
# Assumptions
Your application consists of a single HTML page which bootstraps the application. We will refer
to this page as the chrome.
Your application is divided into several screens (or views) which the user can visit. For example,
the home screen, settings screen, details screen, etc. For each of these screens, we would like to
assign a URL so that it can be bookmarked and later restored. Each of these screens will be
associated with a controller which define the screen's behavior. The most common case is that the
screen will be constructed from an HTML snippet, which we will refer to as the partial. Screens can
have multiple partials, but a single partial is the most common construct. This example makes the
partial boundary visible using a blue line.
You can make a routing table which shows which URL maps to which partial view template and which
controller.
# Example
In this example we have a simple app which consist of two screens:
* Welcome: url `welcome` Show the user contact information.
* Settings: url `settings` Show an edit screen for user contact information.
<example module="deepLinking" deps="angular-route.js, angular-sanitize.js">
<file name="script.js">
angular.module('deepLinking', ['ngRoute', 'ngSanitize'])
.config(function($routeProvider) {
$routeProvider.
when("/welcome", {templateUrl:'welcome.html', controller:WelcomeCntl}).
when("/settings", {templateUrl:'settings.html', controller:SettingsCntl});
});
AppCntl.$inject = ['$scope', '$route']
function AppCntl($scope, $route) {
$scope.$route = $route;
// initialize the model to something useful
$scope.person = {
name:'anonymous',
contacts:[{type:'email', url:'anonymous@example.com'}]
};
}
function WelcomeCntl($scope) {
$scope.greet = function() {
alert("Hello " + $scope.person.name);
};
}
function SettingsCntl($scope, $location) {
$scope.cancel = function() {
$scope.form = angular.copy($scope.person);
};
$scope.save = function() {
angular.copy($scope.form, $scope.person);
$location.path('/welcome');
};
$scope.cancel();
}
</file>
<file name="style.css">
[ng-view] {
border: 1px solid blue;
margin: 0;
padding:1em;
}
.partial-info {
background-color: blue;
color: white;
padding: 3px;
}
</file>
<file name="index.html">
<div ng-controller="AppCntl">
<h1>Your App Chrome</h1>
[ <a href="welcome">Welcome</a> | <a href="settings">Settings</a> ]
<hr/>
<span class="partial-info">
Partial: {{$route.current.template}}
</span>
<div ng-view></div>
<small>Your app footer </small>
</div>
</file>
<file name="settings.html">
<label>Name:</label>
<input type="text" ng:model="form.name" required>
<div ng:repeat="contact in form.contacts">
<select ng:model="contact.type">
<option>url</option>
<option>email</option>
<option>phone</option>
</select>
<input type="text" ng:model="contact.url">
[ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
</div>
<div>
[ <a href="" ng:click="form.contacts.$add()">add</a> ]
</div>
<button ng:click="cancel()">Cancel</button>
<button ng:click="save()">Save</button>
</file>
<file name="welcome.html">
Hello {{person.name}},
<div>
Your contact information:
<div ng:repeat="contact in person.contacts">{{contact.type}}:
<span ng-bind-html="contact.url|linky"></span>
</div>
</div>
</file>
<file name="scenario.js">
it('should navigate to URL', function() {
element('a:contains(Welcome)').click();
expect(element('[ng-view]').text()).toMatch(/Hello anonymous/);
element('a:contains(Settings)').click();
input('form.name').enter('yourname');
element(':button:contains(Save)').click();
element('a:contains(Welcome)').click();
expect(element('[ng-view]').text()).toMatch(/Hello yourname/);
});
</file>
</example>
# Things to notice
* Routes are defined in the `AppCntl` class. The initialization of the controller causes the
initialization of the {@link api/ngRoute.$route $route} service with the proper URL
routes.
* The {@link api/ngRoute.$route $route} service then watches the URL and instantiates the
appropriate controller when the URL changes.
* The {@link api/ngRoute.directive:ngView ngView} widget loads the
view when the URL changes. It also sets the view scope to the newly instantiated controller.
* Changing the URL is sufficient to change the controller and view. It makes no difference whether
the URL is changed programmatically or by the user.

View file

@ -1,114 +0,0 @@
@ngdoc overview
@name Cookbook: Form
@description
A web application's main purpose is to present and gather data. For this reason Angular strives
to make both of these operations trivial. This example shows off how you can build a simple form to
allow a user to enter data.
<doc:example>
<doc:source>
<script>
function FormController($scope) {
var user = $scope.user = {
name: 'John Smith',
address:{line1: '123 Main St.', city:'Anytown', state:'AA', zip:'12345'},
contacts:[{type:'phone', value:'1(234) 555-1212'}]
};
$scope.state = /^\w\w$/;
$scope.zip = /^\d\d\d\d\d$/;
$scope.addContact = function() {
user.contacts.push({type:'email', value:''});
};
$scope.removeContact = function(contact) {
for (var i = 0, ii = user.contacts.length; i < ii; i++) {
if (contact === user.contacts[i]) {
$scope.user.contacts.splice(i, 1);
}
}
};
}
</script>
<div ng-controller="FormController" class="example">
<label>Name:</label><br>
<input type="text" ng-model="user.name" required/> <br><br>
<label>Address:</label><br>
<input type="text" ng-model="user.address.line1" size="33" required> <br>
<input type="text" ng-model="user.address.city" size="12" required>,
<input type="text" ng-model="user.address.state"
ng-pattern="state" size="2" required>
<input type="text" ng-model="user.address.zip" size="5"
ng-pattern="zip" required><br><br>
<label>Phone:</label>
[ <a href="" ng-click="addContact()">add</a> ]
<div ng-repeat="contact in user.contacts">
<select ng-model="contact.type">
<option>email</option>
<option>phone</option>
<option>pager</option>
<option>IM</option>
</select>
<input type="text" ng-model="contact.value" required>
[ <a href="" ng-click="removeContact(contact)">X</a> ]
</div>
<hr/>
Debug View:
<pre>user={{user | json}}</pre>
</div>
</doc:source>
<doc:scenario>
it('should show debug', function() {
expect(binding('user')).toMatch(/John Smith/);
});
it('should add contact', function() {
using('.example').element('a:contains(add)').click();
using('.example div:last').input('contact.value').enter('you@example.org');
expect(binding('user')).toMatch(/\(234\) 555\-1212/);
expect(binding('user')).toMatch(/you@example.org/);
});
it('should remove contact', function() {
using('.example').element('a:contains(X)').click();
expect(binding('user')).not().toMatch(/\(234\) 555\-1212/);
});
it('should validate zip', function() {
expect(using('.example').
element(':input[ng\\:model="user.address.zip"]').
prop('className')).not().toMatch(/ng-invalid/);
using('.example').input('user.address.zip').enter('abc');
expect(using('.example').
element(':input[ng\\:model="user.address.zip"]').
prop('className')).toMatch(/ng-invalid/);
});
it('should validate state', function() {
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
.not().toMatch(/ng-invalid/);
using('.example').input('user.address.state').enter('XXX');
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
.toMatch(/ng-invalid/);
});
</doc:scenario>
</doc:example>
# Things to notice
* The user data model is initialized {@link api/ng.directive:ngController controller} and is
available in the {@link api/ng.$rootScope.Scope scope} with the initial data.
* For debugging purposes we have included a debug view of the model to better understand what
is going on.
* The {@link api/ng.directive:input input directives} simply refer
to the model and are data-bound.
* The inputs validate. (Try leaving them blank or entering non digits in the zip field)
* In your application you can simply read from or write to the model and the form will be updated.
* By clicking the 'add' link you are adding new items into the `user.contacts` array which are then
reflected in the view.

View file

@ -1,39 +0,0 @@
@ngdoc overview
@name Cookbook: Hello World
@description
<doc:example>
<doc:source>
<script>
function HelloCntl($scope) {
$scope.name = 'World';
}
</script>
<div ng-controller="HelloCntl">
Your name: <input type="text" ng-model="name"/>
<hr/>
Hello {{name || "World"}}!
</div>
</doc:source>
<doc:scenario>
it('should change the binding when user enters text', function() {
expect(binding('name')).toEqual('World');
input('name').enter('angular');
expect(binding('name')).toEqual('angular');
});
</doc:scenario>
</doc:example>
# Things to notice
Take a look through the source and note:
* The script tag that {@link guide/bootstrap bootstraps} the Angular environment.
* The text {@link api/ng.directive:input input form control} which is
bound to the greeting name text.
* There is no need for listener registration and event firing on change events.
* The implicit presence of the `name` variable which is in the root {@link api/ng.$rootScope.Scope scope}.
* The double curly brace `{{markup}}`, which binds the name variable to the greeting text.
* The concept of {@link guide/databinding data binding}, which reflects any
changes to the
input field in the greeting text.

View file

@ -1,58 +0,0 @@
@ngdoc overview
@name Cookbook
@description
Welcome to the Angular cookbook. Here we will show you typical uses of Angular by example.
# Hello World
{@link helloworld Hello World}: The simplest possible application that demonstrates the
classic Hello World!
# Basic Form
{@link form Basic Form}: Displaying forms to the user for editing is the bread and butter
of web applications. Angular makes forms easy through bidirectional data binding.
# Advanced Form
{@link advancedform Advanced Form}: Taking the form example to the next level and
providing advanced features such as dirty detection, form reverting and submit disabling if
validation errors exist.
# Model View Controller
{@link mvc MVC}: Tic-Tac-Toe: Model View Controller (MVC) is a time-tested design pattern
to separate the behavior (JavaScript controller) from the presentation (HTML view). This
separation aids in maintainability and testability of your project.
# Multi-page App and Deep Linking
{@link deeplinking Deep Linking}: An AJAX application never navigates away from the
first page it loads. Instead, it changes the DOM of its single page. Eliminating full-page reloads
is what makes AJAX apps responsive, but it creates a problem in that apps with a single URL
prevent you from emailing links to a particular screen within your application.
Deep linking tries to solve this by changing the URL anchor without reloading a page, thus
allowing you to send links to specific screens in your app.
# Services
{@link api/ng Services}: Services are long lived objects in your applications that are
available across controllers. A collection of useful services are pre-bundled with Angular but you
will likely add your own. Services are initialized using dependency injection, which resolves the
order of initialization. This safeguards you from the perils of global state (a common way to
implement long lived objects).
# External Resources
{@link buzz Resources}: Web applications must be able to communicate with the external
services to get and update data. Resources are the abstractions of external URLs which are
specially tailored to Angular data binding.

View file

@ -1,128 +0,0 @@
@ngdoc overview
@name Cookbook: MVC
@description
MVC allows for a clean and testable separation between the behavior (controller) and the view
(HTML template). A Controller is just a JavaScript class which is grafted onto the scope of the
view. This makes it very easy for the controller and the view to share the model.
The model is a set of objects and primitives that are referenced from the Scope ($scope) object.
This makes it very easy to test the controller in isolation since one can simply instantiate the
controller and test without a view, because there is no connection between the controller and the
view.
<doc:example>
<doc:source>
<script>
function TicTacToeCntl($scope, $location) {
$scope.cellStyle= {
'height': '20px',
'width': '20px',
'border': '1px solid black',
'text-align': 'center',
'vertical-align': 'middle',
'cursor': 'pointer'
};
$scope.reset = function() {
$scope.board = [
['', '', ''],
['', '', ''],
['', '', '']
];
$scope.nextMove = 'X';
$scope.winner = '';
setUrl();
};
$scope.dropPiece = function(row, col) {
if (!$scope.winner && !$scope.board[row][col]) {
$scope.board[row][col] = $scope.nextMove;
$scope.nextMove = $scope.nextMove == 'X' ? 'O' : 'X';
setUrl();
}
};
$scope.reset();
$scope.$watch(function() { return $location.search().board;}, readUrl);
function setUrl() {
var rows = [];
angular.forEach($scope.board, function(row) {
rows.push(row.join(','));
});
$location.search({board: rows.join(';') + '/' + $scope.nextMove});
}
function grade() {
var b = $scope.board;
$scope.winner =
row(0) || row(1) || row(2) ||
col(0) || col(1) || col(2) ||
diagonal(-1) || diagonal(1);
function row(row) { return same(b[row][0], b[row][1], b[row][2]);}
function col(col) { return same(b[0][col], b[1][col], b[2][col]);}
function diagonal(i) { return same(b[0][1-i], b[1][1], b[2][1+i]);}
function same(a, b, c) { return (a==b && b==c) ? a : '';};
}
function readUrl(value) {
if (value) {
value = value.split('/');
$scope.nextMove = value[1];
angular.forEach(value[0].split(';'), function(row, col){
$scope.board[col] = row.split(',');
});
grade();
}
}
}
</script>
<h3>Tic-Tac-Toe</h3>
<div ng-controller="TicTacToeCntl">
Next Player: {{nextMove}}
<div class="winner" ng-show="winner">Player {{winner}} has won!</div>
<table class="board">
<tr ng-repeat="row in board track by $index" style="height:15px;">
<td ng-repeat="cell in row track by $index" ng-style="cellStyle"
ng-click="dropPiece($parent.$index, $index)">{{cell}}</td>
</tr>
</table>
<button ng-click="reset()">reset board</button>
</div>
</doc:source>
<doc:scenario>
it('should play a game', function() {
piece(1, 1);
expect(binding('nextMove')).toEqual('O');
piece(3, 1);
expect(binding('nextMove')).toEqual('X');
piece(1, 2);
piece(3, 2);
piece(1, 3);
expect(element('.winner').text()).toEqual('Player X has won!');
});
function piece(row, col) {
element('.board tr:nth-child('+row+') td:nth-child('+col+')').click();
}
</doc:scenario>
</doc:example>
# Things to notice
* The controller is defined in JavaScript and has no reference to the rendering logic.
* The controller is instantiated by Angular and injected into the view.
* The controller can be instantiated in isolation (without a view) and the code will still execute.
This makes it very testable.
* The HTML view is a projection of the model. In the above example, the model is stored in the
board variable.
* All of the controller's properties (such as board and nextMove) are available to the view.
* Changing the model changes the view.
* The view can call any controller function.
* In this example, the `setUrl()` and `readUrl()` functions copy the game state to/from the URL's
hash so the browser's back button will undo game steps. See deep-linking. This example calls {@link
api/ng.$rootScope.Scope#methods_$watch $watch()} to set up a listener that invokes `readUrl()` when needed.

View file

@ -11,7 +11,7 @@ For these reasons binding to event handler attributes (all attributes that start
An example code that would allow XSS vulnerability by evaluating user input in the window context could look like this:
```
<input ng-mode="username">
<input ng-model="username">
<div onclick="{{username}}">click me</div>
```

View file

@ -0,0 +1,10 @@
@ngdoc error
@name $httpBackend:noxhr
@fullName Unsupported XHR
@description
This error occurs in browsers that do not support XmlHttpRequest. AngularJS
supports Safari, Chrome, Firefox, Opera, IE8 and higher, and mobile browsers
(Android, Chrome Mobile, iOS Safari). To avoid this error, use an officially
supported browser.

View file

@ -21,19 +21,19 @@ Below is a quick example of animations being enabled for `ngShow` and `ngHide`:
<label>
<input type="checkbox" ng-model="checked" style="float:left; margin-right:10px;"> Is Visible...
</label>
<div class="check-element animate-show-hide" ng-show="checked" style="clear:both;">
<div class="check-element sample-show-hide" ng-show="checked" style="clear:both;">
Visible...
</div>
</div>
</file>
<file name="animations.css">
.animate-show-hide {
.sample-show-hide {
padding:10px;
border:1px solid black;
background:white;
}
.animate-show-hide.ng-hide-add, .animate-show-hide.ng-hide-remove {
.sample-show-hide.ng-hide-add, .sample-show-hide.ng-hide-remove {
-webkit-transition:all linear 0.5s;
-moz-transition:all linear 0.5s;
-o-transition:all linear 0.5s;
@ -41,13 +41,13 @@ Below is a quick example of animations being enabled for `ngShow` and `ngHide`:
display:block!important;
}
.animate-show-hide.ng-hide-add.ng-hide-add-active,
.animate-show-hide.ng-hide-remove {
.sample-show-hide.ng-hide-add.ng-hide-add-active,
.sample-show-hide.ng-hide-remove {
opacity:0;
}
.animate-show-hide.ng-hide-add,
.animate-show-hide.ng-hide-remove.ng-hide-remove-active {
.sample-show-hide.ng-hide-add,
.sample-show-hide.ng-hide-remove.ng-hide-remove-active {
opacity:1;
}
</file>
@ -258,7 +258,7 @@ The table below explains in detail which animation events are triggered
| {@link api/ng.directive:ngInclude#usage_animations ngInclude} | enter and leave |
| {@link api/ng.directive:ngSwitch#usage_animations ngSwitch} | enter and leave |
| {@link api/ng.directive:ngIf#usage_animations ngIf} | enter and leave |
| {@link api/ng.directive:ngShow#usage_animations ngClass or &#123;&#123;class&#125;&#125;} | add and remove |
| {@link api/ng.directive:ngClass#usage_animations ngClass or &#123;&#123;class&#125;&#125;} | add and remove |
| {@link api/ng.directive:ngShow#usage_animations ngShow & ngHide} | add and remove (the ng-hide class value) |
For a full breakdown of the steps involved during each animation event, refer to the {@link api/ngAnimate.$animate API docs}.

View file

@ -272,8 +272,8 @@ including the configuration of all modules that this module depends on.
In the example above:
The template contains the directive `ng-app="invoice2"`. This tells Angular
to use the `invoice` module as the main module for the application.
The code snippet `angular.module('invoice', ['finance'])` specifies that the `invoice` module depends on the
`finance` module. By this, Angular uses the `InvoiceController` as well as the `currencyConverter` service.
The code snippet `angular.module('invoice2', ['finance2'])` specifies that the `invoice2` module depends on the
`finance2` module. By this, Angular uses the `InvoiceController` as well as the `currencyConverter` service.
Now that Angular knows of all the parts of the application, it needs to create them.
In the previous section we saw that controllers are created using a factory function.

View file

@ -168,7 +168,7 @@ starts with capital letter and ends with "Ctrl" or "Controller".
- Assigning a property to `$scope` creates or updates the model.
- Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method)
- The Controller methods and properties are available in the template (for the `<div>` element and
and its children).
its children).
## Spicy Arguments Example

View file

@ -53,18 +53,19 @@ function myController(scope, notifyService) {
myController.$inject = ['$scope','notify'];
</script>
<div ng-controller="myController">
<div id="simple" ng-controller="myController">
<p>Let's try this simple notify service, injected into the controller...</p>
<input ng-init="message='test'" ng-model="message" >
<button ng-click="callNotify(message);">NOTIFY</button>
<p>(you have to click 3 times to see an alert)</p>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should test service', function() {
expect(element(':input[ng\\:model="message"]').val()).toEqual('test');
expect(element(by.id('simple')).element(by.model('message')).getAttribute('value'))
.toEqual('test');
});
</doc:scenario>
</doc:protractor>
</doc:example>
## Implicit Dependency Injection
@ -95,7 +96,7 @@ function myController($scope, notify) {
};
}
</script>
<div ng-controller="myController">
<div id="implicit" ng-controller="myController">
<p>Let's try the notify service, that is implicitly injected into the controller...</p>
<input ng-init="message='test'" ng-model="message">
<button ng-click="callNotify(message);">NOTIFY</button>

View file

@ -55,7 +55,7 @@ of which depend on other services that are provided by the Angular framework:
function log() {
if (messageQueue.length) {
$log('batchLog messages: ', messageQueue);
$log.log('batchLog messages: ', messageQueue);
messageQueue = [];
}
}

View file

@ -13,7 +13,7 @@ Angular sets these CSS classes. It is up to your application to provide useful s
* `ng-binding`
- **Usage:** angular applies this class to any element that is attached to a data binding, via `ng-bind` or
{{}} curly braces, for example. (see {@link guide/databinding databinding} guide)
`{{}}` curly braces, for example. (see {@link guide/databinding databinding} guide)
* `ng-invalid`, `ng-valid`
- **Usage:** angular applies this class to an input widget element if that element's input does

View file

@ -55,7 +55,7 @@ The following also **matches** `ngModel`:
Angular **normalizes** an element's tag and attribute name to determine which elements match which
directives. We typically refer to directives by their case-sensitive
{@link http://en.wikipedia.org/wiki/CamelCase camelCase} **normalized** name (e.g. `ngModel`).
{@link http://en.wikipedia.org/wiki/CamelCase camelCase} **normalized** name (e.g. `ngModel`).
However, since HTML is case-insensitive, we refer to directives in the DOM by lower-case
forms, typically using {@link http://en.wikipedia.org/wiki/Letter_case#Computers dash-delimited}
attributes on DOM elements (e.g. `ng-model`).
@ -84,10 +84,10 @@ Here are some equivalent examples of elements that match `ngBind`:
<span x-ng-bind="name"></span> <br/>
</div>
</file>
<file name="scenario.js">
<file name="protractorTest.js">
it('should show off bindings', function() {
expect(element('div[ng-controller="Ctrl1"] span[ng-bind]').text())
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 October 4, 1947)');
expect(element(by.css('div[ng-controller="Ctrl1"] span[ng-bind]')).getText())
.toBe('Max Karl Ernst Ludwig Planck (April 23, 1858 October 4, 1947)');
});
</file>
</example>
@ -174,9 +174,9 @@ For example, we could fix the example above by instead writing:
## Creating Directives
First let's talk about the API for registering directives. Much like controllers, directives are
registered on modules. To register a directive, you use the `module.directive` API.
`module.directive` takes the
First let's talk about the {@link api/ng.$compileProvider#methods_directive API for registering directives}. Much like
controllers, directives are registered on modules. To register a directive, you use the
`module.directive` API. `module.directive` takes the
{@link guide/directive#creating-custom-directives_matching-directives normalized} directive name
followed by a **factory function.** This factory function should return an object with the different
options to tell `$compile` how the directive should behave when matched.
@ -527,7 +527,8 @@ where:
* `scope` is an Angular scope object.
* `element` is the jqLite-wrapped element that this directive matches.
* `attrs` is an object with the normalized attribute names and their corresponding values.
* `attrs` is a hash object with key-value pairs of normalized attribute names and their
corresponding attribute values.
In our `link` function, we want to update the displayed time once a second, or whenever a user
changes the time formatting string that our directive binds to. We will use the `$interval` service
@ -734,13 +735,13 @@ own behavior to it.
We want to run the function we pass by invoking it from the directive's scope, but have it run
in the context of the scope where its registered.
We saw earlier how to use `=prop` in the `scope` option, but in the above example, we're using
`&prop` instead. `&` bindings expose a function to an isolated scope allowing the isolated scope
We saw earlier how to use `=attr` in the `scope` option, but in the above example, we're using
`&attr` instead. `&` bindings expose a function to an isolated scope allowing the isolated scope
to invoke it, but maintaining the original scope of the function. So when a user clicks the
`x` in the dialog, it runs `Ctrl`'s `close` function.
`x` in the dialog, it runs `Ctrl`'s `hideDialog` function.
<div class="alert alert-success">
**Best Practice:** use `&prop` in the `scope` option when you want your directive
**Best Practice:** use `&attr` in the `scope` option when you want your directive
to expose an API for binding to behaviors.
</div>

View file

@ -37,11 +37,11 @@ JavaScript, use the {@link api/ng.$rootScope.Scope#methods_$eval `$eval()`} meth
<doc:source>
1+2={{1+2}}
</doc:source>
<doc:scenario>
<doc:protractor>
it('should calculate expression in binding', function() {
expect(binding('1+2')).toEqual('3');
expect(element(by.binding('1+2')).getText()).toEqual('1+2=3');
});
</doc:scenario>
</doc:protractor>
</doc:example>
You can try evaluating different expressions here:
@ -73,14 +73,14 @@ You can try evaluating different expressions here:
</ul>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should allow user expression testing', function() {
element('.expressions :button').click();
var li = using('.expressions ul').repeater('li');
expect(li.count()).toBe(1);
expect(li.row(0)).toEqual(["3*10|currency", "$30.00"]);
element(by.css('.expressions button')).click();
var lis = element(by.css('.expressions ul')).element.all(by.repeater('expr in exprs'));
expect(lis.count()).toBe(1);
expect(lis.get(0).getText()).toEqual('[ X ] 3*10|currency => $30.00');
});
</doc:scenario>
</doc:protractor>
</doc:example>
@ -99,7 +99,7 @@ prevent accidental access to the global state (a common source of subtle bugs).
$scope.name = 'World';
$scope.greet = function() {
($window.mockWindow || $window).alert('Hello ' + $scope.name);
$window.alert('Hello ' + $scope.name);
}
}
</script>
@ -108,21 +108,17 @@ prevent accidental access to the global state (a common source of subtle bugs).
<button ng-click="greet()">Greet</button>
</div>
</doc:source>
<doc:scenario>
it('should calculate expression in binding', function() {
var alertText;
this.addFutureAction('set mock', function($window, $document, done) {
$window.mockWindow = {
alert: function(text){ alertText = text; }
};
done();
});
element(':button:contains(Greet)').click();
expect(this.addFuture('alert text', function(done) {
done(null, alertText);
})).toBe('Hello World');
});
</doc:scenario>
<doc:protractor>
it('should calculate expression in binding', function() {
element(by.css('[ng-click="greet()"]')).click();
var alertDialog = browser.switchTo().alert();
expect(alertDialog.getText()).toEqual('Hello World');
alertDialog.accept();
});
</doc:protractor>
</doc:example>
## Forgiving

View file

@ -121,3 +121,6 @@ text upper-case.
</doc:source>
</doc:example>
## Testing custom filters
See the {@link http://docs.angularjs.org/tutorial/step_09#test phonecat tutorial} for an example.

View file

@ -87,6 +87,16 @@ grunt package
Administrator). This is because `grunt package` creates some symbolic links.
</div>
<div class="alert alert-warning">
**Note:** If you're using Linux, and npm install fails with the message
'Please try running this command again as root/Administrator.', you may need to globally install grunt and bower:
<ul>
<li>sudo npm install -g grunt-cli</li>
<li>sudo npm install -g bower</li>
</ul>
</div>
The build output can be located under the `build` directory. It consists of the following files and
directories:

View file

@ -77,7 +77,7 @@ directory.</p></li>
</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 <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
to run a simple bundled http server: <code>node scripts/web-server.js</code>.</p></li>
</ol>
</div>
@ -106,8 +106,8 @@ directory.</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 <code>scripts\web-server.js</code>, a simple
bundled http server.</p></li>
already installed, you can use <code>node</code> to run a simple
bundled http server: <code>node scripts\web-server.js</code>.</p></li>
</ol>
</div>

View file

@ -73,7 +73,7 @@ angular-seed, and run the application in the browser.
You can now see the page in your browser. It's not very exciting, but that's OK.
The HTML page that displays "Nothing here yet!" was constructed with the HTML code shown below.
The code contains some key Angular elements that we will need going forward.
The code contains some key Angular elements that we will need as we progress.
__`app/index.html`:__
<pre>
@ -104,7 +104,7 @@ __`app/index.html`:__
The `ng-app` attribute represents an Angular directive named `ngApp` (Angular uses
`name-with-dashes` for its custom attributes and `camelCase` for the corresponding directives
that implements them).
which implement them).
This directive is used to flag the html element that Angular should consider to be the root element
of our application.
This gives application developers the freedom to tell Angular if the entire html page or only a

View file

@ -217,7 +217,7 @@ To run the test, do the following:
* Create a new model property in the controller and bind to it from the template. For example:
$scope.name = "World"
$scope.name = "World";
Then add a new binding to `index.html`:

View file

@ -177,7 +177,7 @@ route into the layout template. This makes it a perfect fit for our `index.html`
<div class="alert alert-info">
**Note:** Starting with AngularJS version 1.2, `ngRoute` is in its own module and must be loaded by loading
the `angular-route.js` file distributed with Angular. The easist way to load the file is to add a `<script>`
the `angular-route.js` file distributed with Angular. The easiest way to load the file is to add a `<script>`
tag to your `index.html` file as shown below.
</div>

View file

@ -340,45 +340,58 @@ Although we could do that, let's take the opportunity to learn how to create Jav
__`app/js/animations.js`.__
<pre>
angular.module('phonecatAnimations', ['ngAnimate'])
var phonecatAnimations = angular.module('phonecatAnimations', ['ngAnimate']);
.animation('.phone', function() {
return {
addClass : function(element, className, done) {
if(className != 'active') {
return;
}
element.css({
position: 'absolute',
top: 500,
left: 0,
display: 'block'
});
jQuery(element).animate({
top: 0
}, done);
phonecatAnimations.animation('.phone', function() {
return function(cancel) {
if(cancel) element.stop();
};
},
removeClass : function(element, className, done) {
if(className != 'active') return;
element.css({
position: 'absolute',
left: 0,
top: 0
});
jQuery(element).animate({
top: -500
}, done);
var animateUp = function(element, className, done) {
if(className != 'active') {
return;
}
element.css({
position: 'absolute',
top: 500,
left: 0,
display: 'block'
});
return function(cancel) {
if(cancel) element.stop();
};
jQuery(element).animate({
top: 0
}, done);
return function(cancel) {
if(cancel) {
element.stop();
}
};
});
}
var animateDown = function(element, className, done) {
if(className != 'active') {
return;
}
element.css({
position: 'absolute',
left: 0,
top: 0
});
jQuery(element).animate({
top: -500
}, done);
return function(cancel) {
if(cancel) {
element.stop();
}
};
}
return {
addClass: animateUp,
removeClass: animateDown
};
});
</pre>
Note that we're using {@link http://jquery.com/ jQuery} to implement the animation. jQuery

View file

@ -8,8 +8,6 @@ previous steps using the `git checkout` command.
For more details and examples of the Angular concepts we touched on in this tutorial, see the
{@link guide/ Developer Guide}.
For several more examples of code, see the {@link cookbook/ Cookbook}.
When you are ready to start developing a project using Angular, we recommend that you bootstrap
your development with the {@link https://github.com/angular/angular-seed angular-seed} project.

View file

@ -51,7 +51,7 @@ exports.ngVersions = function() {
});
//match the future version of AngularJS that is set in the package.json file
return expandVersions(sortVersionsNatrually(versions), exports.ngCurrentVersion().full);
return expandVersions(sortVersionsNaturally(versions), exports.ngCurrentVersion().full);
function expandVersions(versions, latestVersion) {
var RC_VERSION = /rc\d/;
@ -87,7 +87,7 @@ exports.ngVersions = function() {
return expanded;
};
function sortVersionsNatrually(versions) {
function sortVersionsNaturally(versions) {
var versionMap = {},
NON_RC_RELEASE_NUMBER = 999;
for(var i = versions.length - 1; i >= 0; i--) {
@ -1401,6 +1401,14 @@ function explainModuleInstallation(moduleName){
modulePackage = 'angular-' + moduleName,
modulePackageFile = modulePackage + '.js';
// Deal with inconsistent ngMock naming - doing it verbosely and explicitly here
// rather than cleverly interweaving it in the previous lines to make it obvious
// what is going on
if ( moduleName == 'mock' ) {
modulePackage = 'angular-mocks';
modulePackageFile = modulePackage + '.js';
}
return '<h1>Installation</h1>' +
'<p>First include <code>' + modulePackageFile +'</code> in your HTML:</p><pre><code>' +
' &lt;script src=&quot;angular.js&quot;&gt;\n' +

View file

@ -16,4 +16,4 @@ RewriteCond %{HTTP_HOST} ^docs-next\.angularjs\.org$
RewriteRule appcache.manifest http://code.angularjs.org/next/docs/appcache.manifest [R=301]
## HTML5 URL Support ##
RewriteRule ^(guide|api|cookbook|misc|tutorial)(/.*)?$ index.html
RewriteRule ^(guide|api|misc|tutorial)(/.*)?$ index.html

View file

@ -246,6 +246,10 @@ ul.events > li > h3 {
text-decoration: none;
}
.tutorial-nav li {
margin-right: 5px;
}
.clear {
clear: both;
}

View file

@ -26,7 +26,7 @@
}
var indexFile = (location.pathname.match(/\/(index[^\.]*\.html)/) || ['', ''])[1],
rUrl = /(#!\/|api|guide|misc|tutorial|cookbook|error|index[^\.]*\.html).*$/,
rUrl = /(#!\/|api|guide|misc|tutorial|error|index[^\.]*\.html).*$/,
baseUrl = location.href.replace(rUrl, indexFile),
jQuery = /index-jq[^\.]*\.html$/.test(baseUrl),
debug = /index[^\.]*-debug\.html$/.test(baseUrl),
@ -334,10 +334,6 @@
<div id="loading" ng-show="loading">Loading...</div>
<div ng-hide="loading" ng-include src="currentPage.partialUrl" onload="afterPartialLoaded()" autoscroll class="content slide-reveal"></div>
<div class="alert alert-info">
<a href="http://blog.angularjs.org/2013/11/farewell-disqus.html">Where did Disqus go?</a>
</div>
</div>
</div>
</div>

View file

@ -111,9 +111,6 @@ docsApp.serviceFactory.docsSearch = ['$rootScope','lunrSearch', 'NG_PAGES',
angular.forEach(index.search(q), function(result) {
var item = NG_PAGES[result.ref];
var section = item.section;
if(section == 'cookbook') {
section = 'tutorial';
}
results[section] = results[section] || [];
if(results[section].length < 15) {
results[section].push(item);
@ -145,11 +142,19 @@ docsApp.directive.docsSearchInput = ['$document',function($document) {
var ESCAPE_KEY_KEYCODE = 27,
FORWARD_SLASH_KEYCODE = 191;
angular.element($document[0].body).bind('keydown', function(event) {
var input = element[0];
if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement != input) {
event.stopPropagation();
event.preventDefault();
input.focus();
if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement) {
var activeElement = document.activeElement;
var activeTagName = activeElement.nodeName.toLowerCase();
var hasInputFocus = activeTagName == 'input' || activeTagName == 'select' ||
activeTagName == 'option' || activeTagName == 'textarea' ||
activeElement.hasAttribute('contenteditable');
if(!hasInputFocus) {
event.stopPropagation();
event.preventDefault();
var input = element[0];
input.focus();
}
}
});
@ -622,7 +627,6 @@ docsApp.serviceFactory.sections = ['NG_PAGES', function sections(NG_PAGES) {
api: [],
tutorial: [],
misc: [],
cookbook: [],
error: [],
getPage: function(sectionId, partialId) {
var pages = sections[sectionId];
@ -667,7 +671,7 @@ docsApp.controller.DocsController = function($scope, $rootScope, $location, $win
}
};
var OFFLINE_COOKIE_NAME = 'ng-offline',
DOCS_PATH = /^\/(api)|(guide)|(cookbook)|(misc)|(tutorial)|(error)/,
DOCS_PATH = /^\/(api)|(guide)|(misc)|(tutorial)|(error)/,
INDEX_PATH = /^(\/|\/index[^\.]*.html)$/,
GLOBALS = /^angular\.([^\.]+)$/,
ERROR = /^([a-zA-Z0-9_$]+:)?([a-zA-Z0-9_$]+)$/,
@ -729,7 +733,6 @@ docsApp.controller.DocsController = function($scope, $rootScope, $location, $win
guide: 'Developer Guide',
misc: 'Miscellaneous',
tutorial: 'Tutorial',
cookbook: 'Examples',
error: 'Error Reference'
};

View file

@ -1,27 +0,0 @@
var sharedConfig = require('./karma-shared.conf');
module.exports = function(config) {
sharedConfig(config, {testName: 'AngularJS: e2e', logFile: 'karma-e2e.log'});
config.set({
frameworks: [],
files: [
'build/angular-scenario.js',
'node_modules/karma-ng-scenario/lib/adapter.js',
'build/docs/docs-scenario.js'
],
proxies: {
// angular.js, angular-resource.js, etc
'/angular': 'http://localhost:8000/build/angular',
'/': 'http://localhost:8000/build/docs/'
},
junitReporter: {
outputFile: 'test_out/e2e.xml',
suite: 'E2E'
},
browserNoActivityTimeout: 90000
});
};

View file

@ -2,6 +2,7 @@ var fs = require('fs');
var shell = require('shelljs');
var grunt = require('grunt');
var spawn = require('child_process').spawn;
var semver = require('semver');
var version;
var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n';
@ -32,31 +33,82 @@ module.exports = {
getVersion: function(){
if (version) return version;
var package = JSON.parse(fs.readFileSync('package.json', 'UTF-8'));
var match = package.version.match(/^([^\-]*)(?:\-(.+))?$/);
var semver = match[1].split('.');
try {
var fullVersion = match[1];
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'
}
if (match[2]) {
fullVersion += '-';
fullVersion += (match[2] == 'snapshot') ? getSnapshotSuffix() : match[2];
var versionParts = semVerVersion.match(/(\d+)\.(\d+)\.(\d+)/);
version = {
full: fullVersion,
major: versionParts[1],
minor: versionParts[2],
dot: versionParts[3],
codename: codeName,
cdn: package.cdnVersion
};
return version;
} catch (e) {
grunt.fail.warn(e);
}
version = {
full: fullVersion,
major: semver[0],
minor: semver[1],
dot: semver[2].replace(/rc\d+/, ''),
codename: package.codename,
cdn: package.cdnVersion
};
function getTagOfCurrentCommit() {
var gitTagResult = shell.exec('git describe --exact-match', {silent:true});
var gitTagOutput = gitTagResult.output.trim();
var branchVersionPattern = new RegExp(package.branchVersion.replace('.', '\\.').replace('*', '\\d+'));
if (gitTagResult.code === 0 && gitTagOutput.match(branchVersionPattern)) {
return gitTagOutput;
} else {
return null;
}
}
return version;
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'+package.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(package.branchVersion.replace(/\*/g, '0'));
}
return semVerVersion;
}
function getSnapshotSuffix() {
var jenkinsBuild = process.env.BUILD_NUMBER || 'local';
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;
}

View file

@ -1,20 +1,27 @@
{
"name": "angularjs",
"version": "1.2.9",
"cdnVersion": "1.2.8",
"codename": "enchanted-articulacy",
"branchVersion": "1.2.*",
"cdnVersion": "1.2.11",
"repository": {
"type": "git",
"url": "https://github.com/angular/angular.js.git"
},
"devDependencies": {
"grunt": "~0.4.1",
"bower": "~1.2.2",
"grunt": "~0.4.2",
"grunt-bump": "~0.0.13",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-compress": "~0.5.2",
"grunt-contrib-connect": "~0.5.0",
"grunt-contrib-compress": "~0.5.2",
"grunt-contrib-copy": "~0.4.1",
"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-merge-conflict": "~0.0.1",
"grunt-parallel": "~0.3.1",
"grunt-shell": "~0.4.0",
"load-grunt-tasks": "~0.3.0",
"bower": "~1.2.2",
"jasmine-node": "~1.11.0",
"q": "~0.9.2",
"q-io": "~1.10.6",
@ -29,16 +36,11 @@
"karma-sauce-launcher": "0.2.0",
"karma-script-launcher": "0.1.0",
"karma-browserstack-launcher": "0.0.7",
"protractor": "~0.16.1",
"protractor": "~0.17.0",
"yaml-js": "~0.0.8",
"marked": "0.2.9",
"rewire": "1.1.3",
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
"grunt-parallel": "~0.3.1",
"grunt-ddescribe-iit": "~0.0.1",
"grunt-merge-conflict": "~0.0.1",
"promises-aplus-tests": "~1.3.2",
"grunt-shell": "~0.4.0",
"semver": "~2.1.0",
"lodash": "~2.1.0",
"browserstacktunnel-wrapper": "~1.1.1"
@ -49,7 +51,5 @@
"url": "https://github.com/angular/angular.js/blob/master/LICENSE"
}
],
"dependencies": {
"grunt-contrib-jshint": "~0.7.2"
}
"dependencies": {}
}

View file

@ -14,6 +14,17 @@ exports.config = {
framework: 'jasmine',
onPrepare: function() {
// Disable animations so e2e tests run more quickly
var disableNgAnimate = function() {
angular.module('disableNgAnimate', []).run(function($animate) {
$animate.enabled(false);
});
};
browser.addMockModule('disableNgAnimate', disableNgAnimate);
},
jasmineNodeOpts: {
defaultTimeoutInterval: 30000
}

View file

@ -1,20 +0,0 @@
#!/bin/bash
echo "############################################"
echo "## Remove "-snapshot" from version ########"
echo "############################################"
ARG_DEFS=()
function run {
cd ../..
replaceJsonProp "package.json" "version" "(.*)-snapshot" "\2"
VERSION=$(readJsonProp "package.json" "version")
git add package.json
git commit -m "chore(release): cut v$VERSION release"
git tag -m "v$VERSION" v$VERSION
}
source $(dirname $0)/../utils.inc

View file

@ -1,24 +0,0 @@
#!/bin/bash
echo "############################################"
echo "## Increment version, add "-snapshot" and set version name ##"
echo "############################################"
ARG_DEFS=(
"--next-version-type=(patch|minor|major)"
"--next-version-name=(.+)"
)
function run {
cd ../..
grunt bump:$NEXT_VERSION_TYPE
NEXT_VERSION=$(readJsonProp "package.json" "version")
replaceJsonProp "package.json" "version" "(.*)" "\2-snapshot"
replaceJsonProp "package.json" "codename" ".*" "$NEXT_VERSION_NAME"
git add package.json
git commit -m "chore(release): start v$NEXT_VERSION ($NEXT_VERSION)"
}
source $(dirname $0)/../utils.inc

View file

@ -1,42 +0,0 @@
#!/bin/bash
# Script for updating angular.js repo from current local build.
echo "#################################"
echo "## Update angular.js ###"
echo "#################################"
ARG_DEFS=(
"--action=(prepare|publish)"
"--next-version-type=(patch|minor|major)"
"--next-version-name=(.+)"
"[--no-test=(true|false)]"
)
function init {
cd ../..
}
function prepare() {
./scripts/angular.js/finalize-version.sh
# Build
if [[ $NO_TEST == "true" ]]; then
npm install --color false
grunt ci-checks package --no-color
else
./jenkins_build.sh
fi
./scripts/angular.js/initialize-new-version.sh --next-version-type=$NEXT_VERSION_TYPE --next-version-name=$NEXT_VERSION_NAME
}
function publish() {
BRANCH=$(git rev-parse --abbrev-ref HEAD)
# push the commits to github
git push origin $BRANCH
# push the release tag
git push origin v`cat build/version.txt`
}
source $(dirname $0)/../utils.inc

View file

@ -0,0 +1,42 @@
#!/bin/bash
# Tags a release
# so that travis can do the actual release.
echo "#################################"
echo "## Tag angular.js for a release #"
echo "#################################"
ARG_DEFS=(
"--action=(prepare|publish)"
"--commit-sha=(.*)"
# the version number of the release.
# e.g. 1.2.12 or 1.2.12-rc.1
"--version-number=([0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?)"
"--version-name=(.+)"
)
function checkVersionNumber() {
BRANCH_PATTERN=$(readJsonProp "package.json" "branchVersion")
if [[ $VERSION_NUMBER != $BRANCH_PATTERN ]]; then
echo "version-number needs to match $BRANCH_PATTERN on this branch"
usage
fi
}
function init {
cd ../..
checkVersionNumber
TAG_NAME="v$VERSION_NUMBER"
}
function prepare() {
git tag "$TAG_NAME" -m "chore(release): $TAG_NAME codename($VERSION_NAME)" "$COMMIT_SHA"
}
function publish() {
# push the tag to github
git push origin $TAG_NAME
}
source $(dirname $0)/../utils.inc

View file

@ -1,38 +1,63 @@
#!/bin/bash
# tags the current commit as a release and publishes all artifacts to
# the different repositories.
# Note: This will also works if the commit is in the past!
echo "#################################"
echo "#### Cut release ################"
echo "#### cut release ############"
echo "#################################"
ARG_DEFS=(
"--next-version-type=(patch|minor|major)"
"--next-version-name=(.+)"
# require the git dryrun flag so the script can't be run without
# thinking about this!
"--git-push-dryrun=(true|false)"
"[--no-test=(true|false)]"
# The sha to release. Needs to be the same as HEAD.
# given as parameter to double check.
"--commit-sha=(.*)"
# the version number of the release.
# e.g. 1.2.12 or 1.2.12-rc.1
"--version-number=([0-9]+\.[0-9]+\.[0-9]+(-[a-z]+\.[0-9]+)?)"
# the codename of the release
"--version-name=(.+)"
)
function init {
NG_ARGS=("$@")
if [[ $(git rev-parse --short HEAD) != $COMMIT_SHA ]]; then
echo "HEAD is not at $COMMIT_SHA"
usage
fi
if [[ ! $VERBOSE ]]; then
VERBOSE=false
fi
if [[ ! $NO_TEST ]]; then
NO_TEST=false
fi
VERBOSE_ARG="--verbose=$VERBOSE"
NO_TEST_ARG="--no_test=$NO_TEST"
}
function build {
cd ../..
npm install --color false
grunt ci-checks package --no-color
cd $SCRIPT_DIR
}
function phase {
ACTION_ARG="--action=$1"
../angular.js/publish.sh $ACTION_ARG $VERBOSE_ARG $NO_TEST_ARG \
--next-version-type=$NEXT_VERSION_TYPE --next-version-name=$NEXT_VERSION_NAME
../angular.js/tag-release.sh $ACTION_ARG $VERBOSE_ARG\
--version-number=$VERSION_NUMBER --version-name=$VERSION_NAME\
--commit-sha=$COMMIT_SHA
if [[ $1 == "prepare" ]]; then
# The build requires the tag to be set already!
build
fi
../code.angularjs.org/publish.sh $ACTION_ARG $VERBOSE_ARG
../bower/publish.sh $ACTION_ARG $VERBOSE_ARG
../angular-seed/publish.sh $ACTION_ARG $VERBOSE_ARG $NO_TEST_ARG
../angular-phonecat/publish.sh $ACTION_ARG $VERBOSE_ARG $NO_TEST_ARG
../angular-seed/publish.sh $ACTION_ARG $VERBOSE_ARG --no-test=true
../angular-phonecat/publish.sh $ACTION_ARG $VERBOSE_ARG --no-test=true
}
function run {

View file

@ -10,7 +10,6 @@ if [ $JOB = "unit" ]; then
grunt test:promises-aplus
grunt test:unit --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_8,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
elif [ $JOB = "e2e" ]; then
grunt test:e2e --browsers SL_Chrome --reporters dots
grunt test:protractor --sauceUser $SAUCE_USERNAME \
--sauceKey $SAUCE_ACCESS_KEY \
--capabilities.tunnel-identifier=$TRAVIS_JOB_NUMBER \

View file

@ -772,7 +772,7 @@ function shallowCopy(src, dst) {
for(var key in src) {
// shallowCopy is only ever called by $compile nodeLinkFn, which has control over src
// so we don't need to worry about using our custom hasOwnProperty here
if (src.hasOwnProperty(key) && key.charAt(0) !== '$' && key.charAt(1) !== '$') {
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
dst[key] = src[key];
}
}

View file

@ -354,11 +354,9 @@ function annotate(fn) {
* @param {(Object|function())} provider If the provider is:
*
* - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
* {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be
* created.
* - `Constructor`: a new instance of the provider will be created using
* {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as
* `object`.
* {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be created.
* - `Constructor`: a new instance of the provider will be created using
* {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`.
*
* @returns {Object} registered provider instance
@ -484,17 +482,16 @@ function annotate(fn) {
* Here is an example of registering a service using
* {@link AUTO.$provide#methods_service $provide.service(class)}.
* <pre>
* $provide.service('ping', ['$http', function($http) {
* var Ping = function() {
* this.$http = $http;
* };
* var Ping = function($http) {
* this.$http = $http;
* };
*
* Ping.$inject = ['$http'];
*
* Ping.prototype.send = function() {
* return this.$http.get('/ping');
* };
*
* return Ping;
* }]);
* Ping.prototype.send = function() {
* return this.$http.get('/ping');
* };
* $provide.service('ping', Ping);
* </pre>
* You would then inject and use this service like this:
* <pre>

View file

@ -175,6 +175,9 @@ function JQLite(element) {
if (element instanceof JQLite) {
return element;
}
if (isString(element)) {
element = trim(element);
}
if (!(this instanceof JQLite)) {
if (isString(element) && element.charAt(0) != '<') {
throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');

View file

@ -424,13 +424,17 @@
<div compile="html"></div>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should auto compile', function() {
expect(element('div[compile]').text()).toBe('Hello Angular');
input('html').enter('{{name}}!');
expect(element('div[compile]').text()).toBe('Angular!');
var textarea = $('textarea');
var output = $('div[compile]');
// The initial state reads 'Hello Angular'.
expect(output.getText()).toBe('Hello Angular');
textarea.clear();
textarea.sendKeys('{{name}}!');
expect(output.getText()).toBe('Angular!');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*
@ -1192,7 +1196,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
hasTranscludeDirective = true;
// Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
// This option should only be used by directives that know how to how to safely handle element transclusion,
// This option should only be used by directives that know how to safely handle element transclusion,
// where the transcluded nodes are added or replaced after linking.
if (!directive.$$tlb) {
assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
@ -1707,9 +1711,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
linkNode = $compileNode[0];
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
var oldClasses = beforeTemplateLinkNode.className;
// it was cloned therefore we have to clone as well.
linkNode = jqLiteClone(compileNode);
replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
// Copy in CSS classes from original node
safeAddClass(jqLite(linkNode), oldClasses);
}
if (afterTemplateNodeLinkFn.transclude) {
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude);

View file

@ -32,11 +32,14 @@ var htmlAnchorDirective = valueFn({
element.append(document.createComment('IE fix'));
}
if (!attr.href && !attr.name) {
if (!attr.href && !attr.xlinkHref && !attr.name) {
return function(scope, element) {
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
'xlink:href' : 'href';
element.on('click', function(event){
// if we have no href url, then don't navigate anywhere.
if (!element.attr('href')) {
if (!element.attr(href)) {
event.preventDefault();
}
});

View file

@ -41,46 +41,48 @@
<a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
<a id="link-6" ng-href="{{value}}">link</a> (link, change location)
</doc:source>
<doc:scenario>
<doc:protractor>
it('should execute ng-click but not reload when href without value', function() {
element('#link-1').click();
expect(input('value').val()).toEqual('1');
expect(element('#link-1').attr('href')).toBe("");
element(by.id('link-1')).click();
expect(element(by.model('value')).getAttribute('value')).toEqual('1');
expect(element(by.id('link-1')).getAttribute('href')).toBe('');
});
it('should execute ng-click but not reload when href empty string', function() {
element('#link-2').click();
expect(input('value').val()).toEqual('2');
expect(element('#link-2').attr('href')).toBe("");
element(by.id('link-2')).click();
expect(element(by.model('value')).getAttribute('value')).toEqual('2');
expect(element(by.id('link-2')).getAttribute('href')).toBe('');
});
it('should execute ng-click and change url when ng-href specified', function() {
expect(element('#link-3').attr('href')).toBe("/123");
expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
element('#link-3').click();
expect(browser().window().path()).toEqual('/123');
element(by.id('link-3')).click();
expect(browser.driver.getCurrentUrl()).toMatch(/\/123$/);
});
it('should execute ng-click but not reload when href empty string and name specified', function() {
element('#link-4').click();
expect(input('value').val()).toEqual('4');
expect(element('#link-4').attr('href')).toBe('');
element(by.id('link-4')).click();
expect(element(by.model('value')).getAttribute('value')).toEqual('4');
expect(element(by.id('link-4')).getAttribute('href')).toBe('');
});
it('should execute ng-click but not reload when no href but name specified', function() {
element('#link-5').click();
expect(input('value').val()).toEqual('5');
expect(element('#link-5').attr('href')).toBe(undefined);
element(by.id('link-5')).click();
expect(element(by.model('value')).getAttribute('value')).toEqual('5');
expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
});
it('should only change url when only ng-href', function() {
input('value').enter('6');
expect(element('#link-6').attr('href')).toBe('6');
element(by.model('value')).clear();
element(by.model('value')).sendKeys('6');
expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
element('#link-6').click();
expect(browser().location().url()).toEqual('/6');
element(by.id('link-6')).click();
expect(browser.getCurrentUrl()).toMatch(/\/6$/);
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
@ -165,13 +167,13 @@
Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
<button ng-model="button" ng-disabled="checked">Button</button>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should toggle button', function() {
expect(element('.doc-example-live :button').prop('disabled')).toBeFalsy();
input('checked').check();
expect(element('.doc-example-live :button').prop('disabled')).toBeTruthy();
expect(element(by.css('.doc-example-live button')).getAttribute('disabled')).toBeFalsy();
element(by.model('checked')).click();
expect(element(by.css('.doc-example-live button')).getAttribute('disabled')).toBeTruthy();
});
</doc:scenario>
</doc:protractor>
</doc:example>
*
* @element INPUT
@ -200,13 +202,13 @@
Check me to check both: <input type="checkbox" ng-model="master"><br/>
<input id="checkSlave" type="checkbox" ng-checked="master">
</doc:source>
<doc:scenario>
<doc:protractor>
it('should check both checkBoxes', function() {
expect(element('.doc-example-live #checkSlave').prop('checked')).toBeFalsy();
input('master').check();
expect(element('.doc-example-live #checkSlave').prop('checked')).toBeTruthy();
expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
element(by.model('master')).click();
expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
});
</doc:scenario>
</doc:protractor>
</doc:example>
*
* @element INPUT
@ -235,13 +237,13 @@
Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
<input type="text" ng-readonly="checked" value="I'm Angular"/>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should toggle readonly attr', function() {
expect(element('.doc-example-live :text').prop('readonly')).toBeFalsy();
input('checked').check();
expect(element('.doc-example-live :text').prop('readonly')).toBeTruthy();
expect(element(by.css('.doc-example-live [type="text"]')).getAttribute('readonly')).toBeFalsy();
element(by.model('checked')).click();
expect(element(by.css('.doc-example-live [type="text"]')).getAttribute('readonly')).toBeTruthy();
});
</doc:scenario>
</doc:protractor>
</doc:example>
*
* @element INPUT
@ -274,13 +276,13 @@
<option id="greet" ng-selected="selected">Greetings!</option>
</select>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should select Greetings!', function() {
expect(element('.doc-example-live #greet').prop('selected')).toBeFalsy();
input('selected').check();
expect(element('.doc-example-live #greet').prop('selected')).toBeTruthy();
expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
element(by.model('selected')).click();
expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
});
</doc:scenario>
</doc:protractor>
</doc:example>
*
* @element OPTION
@ -310,13 +312,13 @@
<summary>Show/Hide me</summary>
</details>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should toggle open', function() {
expect(element('#details').prop('open')).toBeFalsy();
input('open').check();
expect(element('#details').prop('open')).toBeTruthy();
expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
element(by.model('open')).click();
expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
});
</doc:scenario>
</doc:protractor>
</doc:example>
*
* @element DETAILS

View file

@ -305,18 +305,27 @@ function FormController(element, attrs) {
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
</form>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should initialize to model', function() {
expect(binding('userType')).toEqual('guest');
expect(binding('myForm.input.$valid')).toEqual('true');
var userType = element(by.binding('userType'));
var valid = element(by.binding('myForm.input.$valid'));
expect(userType.getText()).toContain('guest');
expect(valid.getText()).toContain('true');
});
it('should be invalid if empty', function() {
input('userType').enter('');
expect(binding('userType')).toEqual('');
expect(binding('myForm.input.$valid')).toEqual('false');
var userType = element(by.binding('userType'));
var valid = element(by.binding('myForm.input.$valid'));
var userInput = element(by.model('userType'));
userInput.clear();
userInput.sendKeys('');
expect(userType.getText()).toEqual('userType =');
expect(valid.getText()).toContain('false');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
var formDirectiveFactory = function(isNgForm) {

View file

@ -9,7 +9,7 @@
*/
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$/;
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/i;
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
var inputType = {
@ -62,29 +62,31 @@ var inputType = {
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
</form>
</doc:source>
<doc:scenario>
<doc:protractor>
var text = element(by.binding('text'));
var valid = element(by.binding('myForm.input.$valid'));
var input = element(by.model('text'));
it('should initialize to model', function() {
expect(binding('text')).toEqual('guest');
expect(binding('myForm.input.$valid')).toEqual('true');
expect(text.getText()).toContain('guest');
expect(valid.getText()).toContain('true');
});
it('should be invalid if empty', function() {
input('text').enter('');
expect(binding('text')).toEqual('');
expect(binding('myForm.input.$valid')).toEqual('false');
input.clear();
input.sendKeys('');
expect(text.getText()).toEqual('text =');
expect(valid.getText()).toContain('false');
});
it('should be invalid if multi word', function() {
input('text').enter('hello world');
expect(binding('myForm.input.$valid')).toEqual('false');
});
input.clear();
input.sendKeys('hello world');
it('should not be trimmed', function() {
input('text').enter('untrimmed ');
expect(binding('text')).toEqual('untrimmed ');
expect(binding('myForm.input.$valid')).toEqual('true');
expect(valid.getText()).toContain('false');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
'text': textInputType,
@ -138,24 +140,30 @@ var inputType = {
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
</form>
</doc:source>
<doc:scenario>
<doc:protractor>
var value = element(by.binding('value'));
var valid = element(by.binding('myForm.input.$valid'));
var input = element(by.model('value'));
it('should initialize to model', function() {
expect(binding('value')).toEqual('12');
expect(binding('myForm.input.$valid')).toEqual('true');
expect(value.getText()).toContain('12');
expect(valid.getText()).toContain('true');
});
it('should be invalid if empty', function() {
input('value').enter('');
expect(binding('value')).toEqual('');
expect(binding('myForm.input.$valid')).toEqual('false');
input.clear();
input.sendKeys('');
expect(value.getText()).toEqual('value =');
expect(valid.getText()).toContain('false');
});
it('should be invalid if over max', function() {
input('value').enter('123');
expect(binding('value')).toEqual('');
expect(binding('myForm.input.$valid')).toEqual('false');
input.clear();
input.sendKeys('123');
expect(value.getText()).toEqual('value =');
expect(valid.getText()).toContain('false');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
'number': numberInputType,
@ -207,23 +215,31 @@ var inputType = {
<tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
</form>
</doc:source>
<doc:scenario>
<doc:protractor>
var text = element(by.binding('text'));
var valid = element(by.binding('myForm.input.$valid'));
var input = element(by.model('text'));
it('should initialize to model', function() {
expect(binding('text')).toEqual('http://google.com');
expect(binding('myForm.input.$valid')).toEqual('true');
expect(text.getText()).toContain('http://google.com');
expect(valid.getText()).toContain('true');
});
it('should be invalid if empty', function() {
input('text').enter('');
expect(binding('text')).toEqual('');
expect(binding('myForm.input.$valid')).toEqual('false');
input.clear();
input.sendKeys('');
expect(text.getText()).toEqual('text =');
expect(valid.getText()).toContain('false');
});
it('should be invalid if not url', function() {
input('text').enter('xxx');
expect(binding('myForm.input.$valid')).toEqual('false');
input.clear();
input.sendKeys('box');
expect(valid.getText()).toContain('false');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
'url': urlInputType,
@ -275,23 +291,30 @@ var inputType = {
<tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
</form>
</doc:source>
<doc:scenario>
<doc:protractor>
var text = element(by.binding('text'));
var valid = element(by.binding('myForm.input.$valid'));
var input = element(by.model('text'));
it('should initialize to model', function() {
expect(binding('text')).toEqual('me@example.com');
expect(binding('myForm.input.$valid')).toEqual('true');
expect(text.getText()).toContain('me@example.com');
expect(valid.getText()).toContain('true');
});
it('should be invalid if empty', function() {
input('text').enter('');
expect(binding('text')).toEqual('');
expect(binding('myForm.input.$valid')).toEqual('false');
input.clear();
input.sendKeys('');
expect(text.getText()).toEqual('text =');
expect(valid.getText()).toContain('false');
});
it('should be invalid if not email', function() {
input('text').enter('xxx');
expect(binding('myForm.input.$valid')).toEqual('false');
input.clear();
input.sendKeys('xxx');
expect(valid.getText()).toContain('false');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
'email': emailInputType,
@ -309,6 +332,8 @@ var inputType = {
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} ngChange Angular expression to be executed when input changes due to user
* interaction with the input element.
* @param {string} ngValue Angular expression which sets the value to which the expression should
* be set when selected.
*
* @example
<doc:example>
@ -316,23 +341,31 @@ var inputType = {
<script>
function Ctrl($scope) {
$scope.color = 'blue';
$scope.specialValue = {
"id": "12345",
"value": "green"
};
}
</script>
<form name="myForm" ng-controller="Ctrl">
<input type="radio" ng-model="color" value="red"> Red <br/>
<input type="radio" ng-model="color" value="green"> Green <br/>
<input type="radio" ng-model="color" ng-value="specialValue"> Green <br/>
<input type="radio" ng-model="color" value="blue"> Blue <br/>
<tt>color = {{color}}</tt><br/>
<tt>color = {{color | json}}</tt><br/>
</form>
Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
</doc:source>
<doc:scenario>
<doc:protractor>
it('should change state', function() {
expect(binding('color')).toEqual('blue');
var color = element(by.binding('color'));
input('color').select('red');
expect(binding('color')).toEqual('red');
expect(color.getText()).toContain('blue');
element.all(by.model('color')).get(0).click();
expect(color.getText()).toContain('red');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
'radio': radioInputType,
@ -369,17 +402,21 @@ var inputType = {
<tt>value2 = {{value2}}</tt><br/>
</form>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should change state', function() {
expect(binding('value1')).toEqual('true');
expect(binding('value2')).toEqual('YES');
var value1 = element(by.binding('value1'));
var value2 = element(by.binding('value2'));
input('value1').check();
input('value2').check();
expect(binding('value1')).toEqual('false');
expect(binding('value2')).toEqual('NO');
expect(value1.getText()).toContain('true');
expect(value2.getText()).toContain('YES');
element(by.model('value1')).click();
element(by.model('value2')).click();
expect(value1.getText()).toContain('false');
expect(value2.getText()).toContain('NO');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
'checkbox': checkboxInputType,
@ -732,44 +769,59 @@ function checkboxInputType(scope, element, attr, ctrl) {
<tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
var user = element(by.binding('{{user}}'));
var userNameValid = element(by.binding('myForm.userName.$valid'));
var lastNameValid = element(by.binding('myForm.lastName.$valid'));
var lastNameError = element(by.binding('myForm.lastName.$error'));
var formValid = element(by.binding('myForm.$valid'));
var userNameInput = element(by.model('user.name'));
var userLastInput = element(by.model('user.last'));
it('should initialize to model', function() {
expect(binding('user')).toEqual('{"name":"guest","last":"visitor"}');
expect(binding('myForm.userName.$valid')).toEqual('true');
expect(binding('myForm.$valid')).toEqual('true');
expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
expect(userNameValid.getText()).toContain('true');
expect(formValid.getText()).toContain('true');
});
it('should be invalid if empty when required', function() {
input('user.name').enter('');
expect(binding('user')).toEqual('{"last":"visitor"}');
expect(binding('myForm.userName.$valid')).toEqual('false');
expect(binding('myForm.$valid')).toEqual('false');
userNameInput.clear();
userNameInput.sendKeys('');
expect(user.getText()).toContain('{"last":"visitor"}');
expect(userNameValid.getText()).toContain('false');
expect(formValid.getText()).toContain('false');
});
it('should be valid if empty when min length is set', function() {
input('user.last').enter('');
expect(binding('user')).toEqual('{"name":"guest","last":""}');
expect(binding('myForm.lastName.$valid')).toEqual('true');
expect(binding('myForm.$valid')).toEqual('true');
userLastInput.clear();
userLastInput.sendKeys('');
expect(user.getText()).toContain('{"name":"guest","last":""}');
expect(lastNameValid.getText()).toContain('true');
expect(formValid.getText()).toContain('true');
});
it('should be invalid if less than required min length', function() {
input('user.last').enter('xx');
expect(binding('user')).toEqual('{"name":"guest"}');
expect(binding('myForm.lastName.$valid')).toEqual('false');
expect(binding('myForm.lastName.$error')).toMatch(/minlength/);
expect(binding('myForm.$valid')).toEqual('false');
userLastInput.clear();
userLastInput.sendKeys('xx');
expect(user.getText()).toContain('{"name":"guest"}');
expect(lastNameValid.getText()).toContain('false');
expect(lastNameError.getText()).toContain('minlength');
expect(formValid.getText()).toContain('false');
});
it('should be invalid if longer than max length', function() {
input('user.last').enter('some ridiculously long name');
expect(binding('user'))
.toEqual('{"name":"guest"}');
expect(binding('myForm.lastName.$valid')).toEqual('false');
expect(binding('myForm.lastName.$error')).toMatch(/maxlength/);
expect(binding('myForm.$valid')).toEqual('false');
userLastInput.clear();
userLastInput.sendKeys('some ridiculously long name');
expect(user.getText()).toContain('{"name":"guest"}');
expect(lastNameValid.getText()).toContain('false');
expect(lastNameError.getText()).toContain('maxlength');
expect(formValid.getText()).toContain('false');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) {
@ -901,14 +953,17 @@ var VALID_CLASS = 'ng-valid',
<textarea ng-model="userContent"></textarea>
</form>
</file>
<file name="scenario.js">
<file name="protractorTest.js">
it('should data-bind and become invalid', function() {
var contentEditable = element('[contenteditable]');
var contentEditable = element(by.css('.doc-example-live [contenteditable]'));
expect(contentEditable.text()).toEqual('Change me!');
input('userContent').enter('');
expect(contentEditable.text()).toEqual('');
expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/);
expect(contentEditable.getText()).toEqual('Change me!');
contentEditable.clear();
contentEditable.sendKeys(protractor.Key.BACK_SPACE);
expect(contentEditable.getText()).toEqual('');
expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
});
</file>
* </example>
@ -1188,7 +1243,10 @@ var ngModelDirective = function() {
* @name ng.directive:ngChange
*
* @description
* Evaluate given expression when user changes the input.
* Evaluate the given expression when the user changes the input.
* The expression is evaluated immediately, unlike the JavaScript onchange event
* which only triggers at the end of a change (usually, when the user leaves the
* form element or presses the return key).
* The expression is not evaluated when the value change is coming from the model.
*
* Note, this directive requires `ngModel` to be present.
@ -1212,24 +1270,30 @@ var ngModelDirective = function() {
* <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
* <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
* <label for="ng-change-example2">Confirmed</label><br />
* debug = {{confirmed}}<br />
* counter = {{counter}}
* <tt>debug = {{confirmed}}</tt><br/>
* <tt>counter = {{counter}}</tt><br/>
* </div>
* </doc:source>
* <doc:scenario>
* <doc:protractor>
* var counter = element(by.binding('counter'));
* var debug = element(by.binding('confirmed'));
*
* it('should evaluate the expression if changing from view', function() {
* expect(binding('counter')).toEqual('0');
* element('#ng-change-example1').click();
* expect(binding('counter')).toEqual('1');
* expect(binding('confirmed')).toEqual('true');
* expect(counter.getText()).toContain('0');
*
* element(by.id('ng-change-example1')).click();
*
* expect(counter.getText()).toContain('1');
* expect(debug.getText()).toContain('true');
* });
*
* it('should not evaluate the expression if changing from model', function() {
* element('#ng-change-example2').click();
* expect(binding('counter')).toEqual('0');
* expect(binding('confirmed')).toEqual('true');
* element(by.id('ng-change-example2')).click();
* expect(counter.getText()).toContain('0');
* expect(debug.getText()).toContain('true');
* });
* </doc:scenario>
* </doc:protractor>
* </doc:example>
*/
var ngChangeDirective = valueFn({
@ -1302,20 +1366,26 @@ var requiredDirective = function() {
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
</form>
</doc:source>
<doc:scenario>
<doc:protractor>
var listInput = element(by.model('names'));
var names = element(by.binding('{{names}}'));
var valid = element(by.binding('myForm.namesInput.$valid'));
var error = element(by.css('span.error'));
it('should initialize to model', function() {
expect(binding('names')).toEqual('["igor","misko","vojta"]');
expect(binding('myForm.namesInput.$valid')).toEqual('true');
expect(element('span.error').css('display')).toBe('none');
expect(names.getText()).toContain('["igor","misko","vojta"]');
expect(valid.getText()).toContain('true');
expect(error.getCssValue('display')).toBe('none');
});
it('should be invalid if empty', function() {
input('names').enter('');
expect(binding('names')).toEqual('');
expect(binding('myForm.namesInput.$valid')).toEqual('false');
expect(element('span.error').css('display')).not().toBe('none');
});
</doc:scenario>
listInput.clear();
listInput.sendKeys('');
expect(names.getText()).toContain('');
expect(valid.getText()).toContain('false');
expect(error.getCssValue('display')).not.toBe('none'); });
</doc:protractor>
</doc:example>
*/
var ngListDirective = function() {
@ -1397,15 +1467,17 @@ var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
<div>You chose {{my.favorite}}</div>
</form>
</doc:source>
<doc:scenario>
<doc:protractor>
var favorite = element(by.binding('my.favorite'));
it('should initialize to model', function() {
expect(binding('my.favorite')).toEqual('unicorns');
expect(favorite.getText()).toContain('unicorns');
});
it('should bind the values to the inputs', function() {
input('my.favorite').select('pizza');
expect(binding('my.favorite')).toEqual('pizza');
element.all(by.model('my.favorite')).get(0).click();
expect(favorite.getText()).toContain('pizza');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
var ngValueDirective = function() {

View file

@ -38,13 +38,17 @@
Hello <span ng-bind="name"></span>!
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should check ng-bind', function() {
expect(using('.doc-example-live').binding('name')).toBe('Whirled');
using('.doc-example-live').input('name').enter('world');
expect(using('.doc-example-live').binding('name')).toBe('world');
var exampleContainer = $('.doc-example-live');
var nameInput = element(by.model('name'));
expect(exampleContainer.findElement(by.binding('name')).getText()).toBe('Whirled');
nameInput.clear();
nameInput.sendKeys('world');
expect(exampleContainer.findElement(by.binding('name')).getText()).toBe('world');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
var ngBindDirective = ngDirective(function(scope, element, attr) {
@ -90,20 +94,22 @@ var ngBindDirective = ngDirective(function(scope, element, attr) {
<pre ng-bind-template="{{salutation}} {{name}}!"></pre>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should check ng-bind', function() {
expect(using('.doc-example-live').binding('salutation')).
toBe('Hello');
expect(using('.doc-example-live').binding('name')).
toBe('World');
using('.doc-example-live').input('salutation').enter('Greetings');
using('.doc-example-live').input('name').enter('user');
expect(using('.doc-example-live').binding('salutation')).
toBe('Greetings');
expect(using('.doc-example-live').binding('name')).
toBe('user');
var salutationElem = element(by.binding('salutation'));
var salutationInput = element(by.model('salutation'));
var nameInput = element(by.model('name'));
expect(salutationElem.getText()).toBe('Hello World!');
salutationInput.clear();
salutationInput.sendKeys('Greetings');
nameInput.clear();
nameInput.sendKeys('user');
expect(salutationElem.getText()).toBe('Greetings user!');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
@ -156,12 +162,10 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
}]);
</file>
<file name="scenario.js">
<file name="protractorTest.js">
it('should check ng-bind-html', function() {
expect(using('.doc-example-live').binding('myHTML')).
toBe(
'I am an <code>HTML</code>string with <a href="#">links!</a> and other <em>stuff</em>'
);
expect(element(by.binding('myHTML')).getText()).toBe(
'I am an HTMLstring with links! and other stuff');
});
</file>
</example>

View file

@ -114,31 +114,34 @@ function classDirective(name, selector) {
color: red;
}
</file>
<file name="scenario.js">
<file name="protractorTest.js">
var ps = element.all(by.css('.doc-example-live p'));
it('should let you toggle the class', function() {
expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/bold/);
expect(element('.doc-example-live p:first').prop('className')).not().toMatch(/red/);
expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
expect(ps.first().getAttribute('class')).not.toMatch(/red/);
input('important').check();
expect(element('.doc-example-live p:first').prop('className')).toMatch(/bold/);
element(by.model('important')).click();
expect(ps.first().getAttribute('class')).toMatch(/bold/);
input('error').check();
expect(element('.doc-example-live p:first').prop('className')).toMatch(/red/);
element(by.model('error')).click();
expect(ps.first().getAttribute('class')).toMatch(/red/);
});
it('should let you toggle string example', function() {
expect(element('.doc-example-live p:nth-of-type(2)').prop('className')).toBe('');
input('style').enter('red');
expect(element('.doc-example-live p:nth-of-type(2)').prop('className')).toBe('red');
expect(ps.get(1).getAttribute('class')).toBe('');
element(by.model('style')).clear();
element(by.model('style')).sendKeys('red');
expect(ps.get(1).getAttribute('class')).toBe('red');
});
it('array example should have 3 classes', function() {
expect(element('.doc-example-live p:last').prop('className')).toBe('');
input('style1').enter('bold');
input('style2').enter('strike');
input('style3').enter('red');
expect(element('.doc-example-live p:last').prop('className')).toBe('bold strike red');
expect(ps.last().getAttribute('class')).toBe('');
element(by.model('style1')).sendKeys('bold');
element(by.model('style2')).sendKeys('strike');
element(by.model('style3')).sendKeys('red');
expect(ps.last().getAttribute('class')).toBe('bold strike red');
});
</file>
</example>
@ -149,8 +152,8 @@ function classDirective(name, selector) {
<example animations="true">
<file name="index.html">
<input type="button" value="set" ng-click="myVar='my-class'">
<input type="button" value="clear" ng-click="myVar=''">
<input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
<input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
<br>
<span class="base-class" ng-class="myVar">Sample Text</span>
</file>
@ -165,19 +168,19 @@ function classDirective(name, selector) {
font-size:3em;
}
</file>
<file name="scenario.js">
<file name="protractorTest.js">
it('should check ng-class', function() {
expect(element('.doc-example-live span').prop('className')).not().
expect(element(by.css('.base-class')).getAttribute('class')).not.
toMatch(/my-class/);
using('.doc-example-live').element(':button:first').click();
element(by.id('setbtn')).click();
expect(element('.doc-example-live span').prop('className')).
expect(element(by.css('.base-class')).getAttribute('class')).
toMatch(/my-class/);
using('.doc-example-live').element(':button:last').click();
element(by.id('clearbtn')).click();
expect(element('.doc-example-live span').prop('className')).not().
expect(element(by.css('.base-class')).getAttribute('class')).not.
toMatch(/my-class/);
});
</file>
@ -229,11 +232,11 @@ var ngClassDirective = classDirective('', true);
color: blue;
}
</file>
<file name="scenario.js">
<file name="protractorTest.js">
it('should check ng-class-odd and ng-class-even', function() {
expect(element('.doc-example-live li:first span').prop('className')).
expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
toMatch(/odd/);
expect(element('.doc-example-live li:last span').prop('className')).
expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
toMatch(/even/);
});
</file>
@ -277,11 +280,11 @@ var ngClassOddDirective = classDirective('Odd', 0);
color: blue;
}
</file>
<file name="scenario.js">
<file name="protractorTest.js">
it('should check ng-class-odd and ng-class-even', function() {
expect(element('.doc-example-live li:first span').prop('className')).
expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
toMatch(/odd/);
expect(element('.doc-example-live li:last span').prop('className')).
expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
toMatch(/even/);
});
</file>

View file

@ -45,14 +45,14 @@
<div id="template1" ng-cloak>{{ 'hello' }}</div>
<div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should remove the template directive and css class', function() {
expect(element('.doc-example-live #template1').attr('ng-cloak')).
not().toBeDefined();
expect(element('.doc-example-live #template2').attr('ng-cloak')).
not().toBeDefined();
expect($('.doc-example-live #template1').getAttribute('ng-cloak')).
toBeNull();
expect($('.doc-example-live #template2').getAttribute('ng-cloak')).
toBeNull();
});
</doc:scenario>
</doc:protractor>
</doc:example>
*
*/

View file

@ -82,22 +82,36 @@
</ul>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should check controller as', function() {
expect(element('#ctrl-as-exmpl>:input').val()).toBe('John Smith');
expect(element('#ctrl-as-exmpl li:nth-child(1) input').val())
.toBe('408 555 1212');
expect(element('#ctrl-as-exmpl li:nth-child(2) input').val())
.toBe('john.smith@example.org');
var container = element(by.id('ctrl-as-exmpl'));
element('#ctrl-as-exmpl li:first a:contains("clear")').click();
expect(element('#ctrl-as-exmpl li:first input').val()).toBe('');
expect(container.findElement(by.model('settings.name'))
.getAttribute('value')).toBe('John Smith');
element('#ctrl-as-exmpl li:last a:contains("add")').click();
expect(element('#ctrl-as-exmpl li:nth-child(3) input').val())
.toBe('yourname@example.org');
var firstRepeat =
container.findElement(by.repeater('contact in settings.contacts').row(0));
var secondRepeat =
container.findElement(by.repeater('contact in settings.contacts').row(1));
expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
.toBe('408 555 1212');
expect(secondRepeat.findElement(by.model('contact.value')).getAttribute('value'))
.toBe('john.smith@example.org');
firstRepeat.findElement(by.linkText('clear')).click()
expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
.toBe('');
container.findElement(by.linkText('add')).click();
expect(container.findElement(by.repeater('contact in settings.contacts').row(2))
.findElement(by.model('contact.value'))
.getAttribute('value'))
.toBe('yourname@example.org');
});
</doc:scenario>
</doc:protractor>
</doc:example>
<doc:example>
<doc:source>
@ -145,22 +159,36 @@
</ul>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should check controller', function() {
expect(element('#ctrl-exmpl>:input').val()).toBe('John Smith');
expect(element('#ctrl-exmpl li:nth-child(1) input').val())
.toBe('408 555 1212');
expect(element('#ctrl-exmpl li:nth-child(2) input').val())
.toBe('john.smith@example.org');
var container = element(by.id('ctrl-exmpl'));
element('#ctrl-exmpl li:first a:contains("clear")').click();
expect(element('#ctrl-exmpl li:first input').val()).toBe('');
expect(container.findElement(by.model('name'))
.getAttribute('value')).toBe('John Smith');
element('#ctrl-exmpl li:last a:contains("add")').click();
expect(element('#ctrl-exmpl li:nth-child(3) input').val())
.toBe('yourname@example.org');
var firstRepeat =
container.findElement(by.repeater('contact in contacts').row(0));
var secondRepeat =
container.findElement(by.repeater('contact in contacts').row(1));
expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
.toBe('408 555 1212');
expect(secondRepeat.findElement(by.model('contact.value')).getAttribute('value'))
.toBe('john.smith@example.org');
firstRepeat.findElement(by.linkText('clear')).click()
expect(firstRepeat.findElement(by.model('contact.value')).getAttribute('value'))
.toBe('');
container.findElement(by.linkText('add')).click();
expect(container.findElement(by.repeater('contact in contacts').row(2))
.findElement(by.model('contact.value'))
.getAttribute('value'))
.toBe('yourname@example.org');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/

View file

@ -9,6 +9,7 @@
* an element is clicked.
*
* @element ANY
* @priority 0
* @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
* click. (Event object is available as `$event`)
*
@ -65,6 +66,7 @@ forEach(
* The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
*
* @element ANY
* @priority 0
* @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
* a dblclick. (The Event object is available as `$event`)
*
@ -88,6 +90,7 @@ forEach(
* The ngMousedown directive allows you to specify custom behavior on mousedown event.
*
* @element ANY
* @priority 0
* @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
* mousedown. (Event object is available as `$event`)
*
@ -111,6 +114,7 @@ forEach(
* Specify custom behavior on mouseup event.
*
* @element ANY
* @priority 0
* @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
* mouseup. (Event object is available as `$event`)
*
@ -133,6 +137,7 @@ forEach(
* Specify custom behavior on mouseover event.
*
* @element ANY
* @priority 0
* @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
* mouseover. (Event object is available as `$event`)
*
@ -156,6 +161,7 @@ forEach(
* Specify custom behavior on mouseenter event.
*
* @element ANY
* @priority 0
* @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
* mouseenter. (Event object is available as `$event`)
*
@ -179,6 +185,7 @@ forEach(
* Specify custom behavior on mouseleave event.
*
* @element ANY
* @priority 0
* @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
* mouseleave. (Event object is available as `$event`)
*
@ -202,6 +209,7 @@ forEach(
* Specify custom behavior on mousemove event.
*
* @element ANY
* @priority 0
* @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
* mousemove. (Event object is available as `$event`)
*
@ -225,6 +233,7 @@ forEach(
* Specify custom behavior on keydown event.
*
* @element ANY
* @priority 0
* @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
* keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
*
@ -246,6 +255,7 @@ forEach(
* Specify custom behavior on keyup event.
*
* @element ANY
* @priority 0
* @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
* keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
*
@ -292,6 +302,7 @@ forEach(
* attribute**.
*
* @element form
* @priority 0
* @param {expression} ngSubmit {@link guide/expression Expression} to eval. (Event object is available as `$event`)
*
* @example
@ -316,20 +327,20 @@ forEach(
<pre>list={{list}}</pre>
</form>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should check ng-submit', function() {
expect(binding('list')).toBe('[]');
element('.doc-example-live #submit').click();
expect(binding('list')).toBe('["hello"]');
expect(input('text').val()).toBe('');
expect(element(by.binding('list')).getText()).toBe('list=[]');
element(by.css('.doc-example-live #submit')).click();
expect(element(by.binding('list')).getText()).toContain('hello');
expect(element(by.input('text')).getAttribute('value')).toBe('');
});
it('should ignore empty strings', function() {
expect(binding('list')).toBe('[]');
element('.doc-example-live #submit').click();
element('.doc-example-live #submit').click();
expect(binding('list')).toBe('["hello"]');
});
</doc:scenario>
expect(element(by.binding('list')).getText()).toBe('list=[]');
element(by.css('.doc-example-live #submit')).click();
element(by.css('.doc-example-live #submit')).click();
expect(element(by.binding('list')).getText()).toContain('hello');
});
</doc:protractor>
</doc:example>
*/
@ -341,6 +352,7 @@ forEach(
* Specify custom behavior on focus event.
*
* @element window, input, select, textarea, a
* @priority 0
* @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
* focus. (Event object is available as `$event`)
*
@ -356,6 +368,7 @@ forEach(
* Specify custom behavior on blur event.
*
* @element window, input, select, textarea, a
* @priority 0
* @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
* blur. (Event object is available as `$event`)
*
@ -371,6 +384,7 @@ forEach(
* Specify custom behavior on copy event.
*
* @element window, input, select, textarea, a
* @priority 0
* @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
* copy. (Event object is available as `$event`)
*
@ -391,6 +405,7 @@ forEach(
* Specify custom behavior on cut event.
*
* @element window, input, select, textarea, a
* @priority 0
* @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
* cut. (Event object is available as `$event`)
*
@ -411,6 +426,7 @@ forEach(
* Specify custom behavior on paste event.
*
* @element window, input, select, textarea, a
* @priority 0
* @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
* paste. (Event object is available as `$event`)
*

View file

@ -110,19 +110,24 @@
top:50px;
}
</file>
<file name="scenario.js">
<file name="protractorTest.js">
var templateSelect = element(by.model('template'));
var includeElem = element(by.css('.doc-example-live [ng-include]'));
it('should load template1.html', function() {
expect(element('.doc-example-live [ng-include]').text()).
toMatch(/Content of template1.html/);
expect(includeElem.getText()).toMatch(/Content of template1.html/);
});
it('should load template2.html', function() {
select('template').option('1');
expect(element('.doc-example-live [ng-include]').text()).
toMatch(/Content of template2.html/);
templateSelect.click();
templateSelect.element.all(by.css('option')).get(2).click();
expect(includeElem.getText()).toMatch(/Content of template2.html/);
});
it('should change to blank', function() {
select('template').option('');
expect(element('.doc-example-live [ng-include]')).toBe(undefined);
templateSelect.click();
templateSelect.element.all(by.css('option')).get(0).click();
expect(includeElem.isPresent()).toBe(false);
});
</file>
</example>

View file

@ -15,6 +15,13 @@
* should use {@link guide/controller controllers} rather than `ngInit`
* to initialize values on a scope.
* </div>
* <div class="alert alert-warning">
* **Note**: If you have assignment in `ngInit` along with {@link api/ng.$filter `$filter`}, make
* sure you have parenthesis for correct precedence:
* <pre class="prettyprint">
* <div ng-init="test1 = (data | orderBy:'name')"></div>
* </pre>
* </div>
*
* @priority 450
*
@ -37,15 +44,15 @@
</div>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should alias index positions', function() {
expect(element('.example-init').text())
.toBe('list[ 0 ][ 0 ] = a;' +
'list[ 0 ][ 1 ] = b;' +
'list[ 1 ][ 0 ] = c;' +
'list[ 1 ][ 1 ] = d;');
var elements = element.all(by.css('.example-init'));
expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
var ngInitDirective = ngDirective({

View file

@ -24,13 +24,12 @@
<div>Normal: {{1 + 2}}</div>
<div ng-non-bindable>Ignored: {{1 + 2}}</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should check ng-non-bindable', function() {
expect(using('.doc-example-live').binding('1 + 2')).toBe('3');
expect(using('.doc-example-live').element('div:last').text()).
toMatch(/1 \+ 2/);
expect(element(by.binding('1 + 2')).getText()).toContain('3');
expect(element.all(by.css('.doc-example-live div')).last().getText()).toMatch(/1 \+ 2/);
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });

View file

@ -123,49 +123,53 @@
</ng-pluralize>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should show correct pluralized string', function() {
expect(element('.doc-example-live ng-pluralize:first').text()).
toBe('1 person is viewing.');
expect(element('.doc-example-live ng-pluralize:last').text()).
toBe('Igor is viewing.');
var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
var withOffset = element.all(by.css('ng-pluralize')).get(1);
var countInput = element(by.model('personCount'));
using('.doc-example-live').input('personCount').enter('0');
expect(element('.doc-example-live ng-pluralize:first').text()).
toBe('Nobody is viewing.');
expect(element('.doc-example-live ng-pluralize:last').text()).
toBe('Nobody is viewing.');
expect(withoutOffset.getText()).toEqual('1 person is viewing.');
expect(withOffset.getText()).toEqual('Igor is viewing.');
using('.doc-example-live').input('personCount').enter('2');
expect(element('.doc-example-live ng-pluralize:first').text()).
toBe('2 people are viewing.');
expect(element('.doc-example-live ng-pluralize:last').text()).
toBe('Igor and Misko are viewing.');
countInput.clear();
countInput.sendKeys('0');
using('.doc-example-live').input('personCount').enter('3');
expect(element('.doc-example-live ng-pluralize:first').text()).
toBe('3 people are viewing.');
expect(element('.doc-example-live ng-pluralize:last').text()).
toBe('Igor, Misko and one other person are viewing.');
expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
expect(withOffset.getText()).toEqual('Nobody is viewing.');
using('.doc-example-live').input('personCount').enter('4');
expect(element('.doc-example-live ng-pluralize:first').text()).
toBe('4 people are viewing.');
expect(element('.doc-example-live ng-pluralize:last').text()).
toBe('Igor, Misko and 2 other people are viewing.');
countInput.clear();
countInput.sendKeys('2');
expect(withoutOffset.getText()).toEqual('2 people are viewing.');
expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
countInput.clear();
countInput.sendKeys('3');
expect(withoutOffset.getText()).toEqual('3 people are viewing.');
expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
countInput.clear();
countInput.sendKeys('4');
expect(withoutOffset.getText()).toEqual('4 people are viewing.');
expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
});
it('should show data-binded names', function() {
using('.doc-example-live').input('personCount').enter('4');
expect(element('.doc-example-live ng-pluralize:last').text()).
toBe('Igor, Misko and 2 other people are viewing.');
using('.doc-example-live').input('person1').enter('Di');
using('.doc-example-live').input('person2').enter('Vojta');
expect(element('.doc-example-live ng-pluralize:last').text()).
toBe('Di, Vojta and 2 other people are viewing.');
it('should show data-bound names', function() {
var withOffset = element.all(by.css('ng-pluralize')).get(1);
var personCount = element(by.model('personCount'));
var person1 = element(by.model('person1'));
var person2 = element(by.model('person2'));
personCount.clear();
personCount.sendKeys('4');
person1.clear();
person1.sendKeys('Di');
person2.clear();
person2.sendKeys('Vojta');
expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {

View file

@ -172,25 +172,27 @@
max-height:40px;
}
</file>
<file name="scenario.js">
it('should render initial data set', function() {
var r = using('.doc-example-live').repeater('ul li');
expect(r.count()).toBe(10);
expect(r.row(0)).toEqual(["1","John","25"]);
expect(r.row(1)).toEqual(["2","Jessie","30"]);
expect(r.row(9)).toEqual(["10","Samantha","60"]);
expect(binding('friends.length')).toBe("10");
});
<file name="protractorTest.js">
var friends = element(by.css('.doc-example-live'))
.element.all(by.repeater('friend in friends'));
it('should render initial data set', function() {
expect(friends.count()).toBe(10);
expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
expect(element(by.binding('friends.length')).getText())
.toMatch("I have 10 friends. They are:");
});
it('should update repeater when filter predicate changes', function() {
var r = using('.doc-example-live').repeater('ul li');
expect(r.count()).toBe(10);
expect(friends.count()).toBe(10);
input('q').enter('ma');
element(by.css('.doc-example-live')).element(by.model('q')).sendKeys('ma');
expect(r.count()).toBe(2);
expect(r.row(0)).toEqual(["1","Mary","28"]);
expect(r.row(1)).toEqual(["2","Samantha","60"]);
expect(friends.count()).toBe(2);
expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
});
</file>
</example>

View file

@ -52,6 +52,11 @@
*
* Just remember to include the important flag so the CSS override will function.
*
* <div class="alert alert-warning">
* **Note:** Here is a list of values that ngShow will consider as a falsy value (case insensitive):<br />
* "f" / "0" / "false" / "no" / "n" / "[]"
* </div>
*
* ## A note about animations with ngShow
*
* Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
@ -127,16 +132,19 @@
background:white;
}
</file>
<file name="scenario.js">
it('should check ng-show / ng-hide', function() {
expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
<file name="protractorTest.js">
var thumbsUp = element(by.css('.doc-example-live span.icon-thumbs-up'));
var thumbsDown = element(by.css('.doc-example-live span.icon-thumbs-down'));
input('checked').check();
it('should check ng-show / ng-hide', function() {
expect(thumbsUp.isDisplayed()).toBeFalsy();
expect(thumbsDown.isDisplayed()).toBeTruthy();
expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
});
element(by.model('checked')).click();
expect(thumbsUp.isDisplayed()).toBeTruthy();
expect(thumbsDown.isDisplayed()).toBeFalsy();
});
</file>
</example>
*/
@ -200,6 +208,11 @@ var ngShowDirective = ['$animate', function($animate) {
* </pre>
*
* Just remember to include the important flag so the CSS override will function.
*
* <div class="alert alert-warning">
* **Note:** Here is a list of values that ngHide will consider as a falsy value (case insensitive):<br />
* "f" / "0" / "false" / "no" / "n" / "[]"
* </div>
*
* ## A note about animations with ngHide
*
@ -276,16 +289,19 @@ var ngShowDirective = ['$animate', function($animate) {
background:white;
}
</file>
<file name="scenario.js">
it('should check ng-show / ng-hide', function() {
expect(element('.doc-example-live .check-element:first:hidden').count()).toEqual(1);
expect(element('.doc-example-live .check-element:last:visible').count()).toEqual(1);
<file name="protractorTest.js">
var thumbsUp = element(by.css('.doc-example-live span.icon-thumbs-up'));
var thumbsDown = element(by.css('.doc-example-live span.icon-thumbs-down'));
input('checked').check();
it('should check ng-show / ng-hide', function() {
expect(thumbsUp.isDisplayed()).toBeFalsy();
expect(thumbsDown.isDisplayed()).toBeTruthy();
expect(element('.doc-example-live .check-element:first:visible').count()).toEqual(1);
expect(element('.doc-example-live .check-element:last:hidden').count()).toEqual(1);
});
element(by.model('checked')).click();
expect(thumbsUp.isDisplayed()).toBeTruthy();
expect(thumbsDown.isDisplayed()).toBeFalsy();
});
</file>
</example>
*/

View file

@ -27,13 +27,15 @@
color: black;
}
</file>
<file name="scenario.js">
<file name="protractorTest.js">
var colorSpan = element(by.css('.doc-example-live span'));
it('should check ng-style', function() {
expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
element('.doc-example-live :button[value=set]').click();
expect(element('.doc-example-live span').css('color')).toBe('rgb(255, 0, 0)');
element('.doc-example-live :button[value=clear]').click();
expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
element(by.css('.doc-example-live input[value=set]')).click();
expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
element(by.css('.doc-example-live input[value=clear]')).click();
expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
});
</file>
</example>

View file

@ -11,7 +11,7 @@
* as specified in the template.
*
* The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
* from the template cache), `ngSwitch` simply choses one of the nested elements and makes it visible based on which element
* from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
* matches the value obtained from the evaluated expression. In other words, you define a container element
* (where you place the directive), place an expression on the **`on="..."` attribute**
* (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
@ -107,17 +107,20 @@
top:0;
}
</file>
<file name="scenario.js">
<file name="protractorTest.js">
var switchElem = element(by.css('.doc-example-live [ng-switch]'));
var select = element(by.model('selection'));
it('should start in settings', function() {
expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Settings Div/);
expect(switchElem.getText()).toMatch(/Settings Div/);
});
it('should change to home', function() {
select('selection').option('home');
expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Home Span/);
select.element.all(by.css('option')).get(1).click();
expect(switchElem.getText()).toMatch(/Home Span/);
});
it('should select default', function() {
select('selection').option('other');
expect(element('.doc-example-live [ng-switch]').text()).toMatch(/default/);
select.element.all(by.css('option')).get(2).click();
expect(switchElem.getText()).toMatch(/default/);
});
</file>
</example>

View file

@ -40,14 +40,18 @@
<pane title="{{title}}">{{text}}</pane>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should have transcluded', function() {
input('title').enter('TITLE');
input('text').enter('TEXT');
expect(binding('title')).toEqual('TITLE');
expect(binding('text')).toEqual('TEXT');
var titleElement = element(by.model('title'));
titleElement.clear();
titleElement.sendKeys('TITLE');
var textElement = element(by.model('text'));
textElement.clear();
textElement.sendKeys('TEXT');
expect(element(by.binding('title')).getText()).toEqual('TITLE');
expect(element(by.binding('text')).getText()).toEqual('TEXT');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*
*/

View file

@ -25,12 +25,12 @@
<a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
<div id="tpl-content" ng-include src="currentTpl"></div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should load template defined inside script tag', function() {
element('#tpl-link').click();
expect(element('#tpl-content').text()).toMatch(/Content of the template/);
element(by.css('#tpl-link')).click();
expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
var scriptDirective = ['$templateCache', function($templateCache) {

View file

@ -19,14 +19,21 @@ var ngOptionsMinErr = minErr('ngOptions');
* represented by the selected option will be bound to the model identified by the `ngModel`
* directive.
*
* <div class="alert alert-warning">
* **Note:** `ngModel` compares by reference, not value. This is important when binding to an
* array of objects. See an example {@link http://jsfiddle.net/qWzTb/ in this jsfiddle}.
* </div>
*
* Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
* be nested into the `<select>` element. This element will then represent the `null` or "not selected"
* option. See example below for demonstration.
*
* Note: `ngOptions` provides iterator facility for `<option>` element which should be used instead
* <div class="alert alert-warning">
* **Note:** `ngOptions` provides an iterator facility for the `<option>` element which should be used instead
* of {@link ng.directive:ngRepeat ngRepeat} when you want the
* `select` model to be bound to a non-string value. This is because an option element can only
* be bound to string values at present.
* </div>
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
@ -113,15 +120,17 @@ var ngOptionsMinErr = minErr('ngOptions');
</div>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should check ng-options', function() {
expect(binding('{selected_color:color}')).toMatch('red');
select('color').option('0');
expect(binding('{selected_color:color}')).toMatch('black');
using('.nullable').select('color').option('');
expect(binding('{selected_color:color}')).toMatch('null');
expect(element(by.binding('{selected_color:color}')).getText()).toMatch('red');
element.all(by.select('color')).first().click();
element.all(by.css('select[ng-model="color"] option')).first().click();
expect(element(by.binding('{selected_color:color}')).getText()).toMatch('black');
element(by.css('.nullable select[ng-model="color"]')).click();
element.all(by.css('.nullable select[ng-model="color"] option')).first().click();
expect(element(by.binding('{selected_color:color}')).getText()).toMatch('null');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
@ -430,7 +439,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
// We now build up the list of options we need (we merge later)
for (index = 0; length = keys.length, index < length; index++) {
key = index;
if (keyName) {
key = keys[index];

View file

@ -70,35 +70,47 @@
Equality <input type="checkbox" ng-model="strict"><br>
<table id="searchObjResults">
<tr><th>Name</th><th>Phone</th></tr>
<tr ng-repeat="friend in friends | filter:search:strict">
<td>{{friend.name}}</td>
<td>{{friend.phone}}</td>
<tr ng-repeat="friendObj in friends | filter:search:strict">
<td>{{friendObj.name}}</td>
<td>{{friendObj.phone}}</td>
</tr>
</table>
</doc:source>
<doc:scenario>
it('should search across all fields when filtering with a string', function() {
input('searchText').enter('m');
expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')).
toEqual(['Mary', 'Mike', 'Adam']);
<doc:protractor>
var expectFriendNames = function(expectedNames, key) {
element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
arr.forEach(function(wd, i) {
expect(wd.getText()).toMatch(expectedNames[i]);
});
});
};
input('searchText').enter('76');
expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')).
toEqual(['John', 'Julie']);
it('should search across all fields when filtering with a string', function() {
var searchText = element(by.model('searchText'));
searchText.clear();
searchText.sendKeys('m');
expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
searchText.clear();
searchText.sendKeys('76');
expectFriendNames(['John', 'Julie'], 'friend');
});
it('should search in specific fields when filtering with a predicate object', function() {
input('search.$').enter('i');
expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')).
toEqual(['Mary', 'Mike', 'Julie', 'Juliette']);
var searchAny = element(by.model('search.$'));
searchAny.clear();
searchAny.sendKeys('i');
expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
});
it('should use a equal comparison when comparator is true', function() {
input('search.name').enter('Julie');
input('strict').check();
expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')).
toEqual(['Julie']);
var searchName = element(by.model('search.name'));
var strict = element(by.model('strict'));
searchName.clear();
searchName.sendKeys('Julie');
strict.click();
expectFriendNames(['Julie'], 'friendObj');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
function filterFilter() {
@ -176,7 +188,7 @@ function filterFilter() {
(function(path) {
if (typeof expression[path] == 'undefined') return;
predicates.push(function(value) {
return search(path == '$' ? value : getter(value, path), expression[path]);
return search(path == '$' ? value : (value && value[path]), expression[path]);
});
})(key);
}

View file

@ -24,21 +24,22 @@
</script>
<div ng-controller="Ctrl">
<input type="number" ng-model="amount"> <br>
default currency symbol ($): {{amount | currency}}<br>
custom currency identifier (USD$): {{amount | currency:"USD$"}}
default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
custom currency identifier (USD$): <span>{{amount | currency:"USD$"}}</span>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should init with 1234.56', function() {
expect(binding('amount | currency')).toBe('$1,234.56');
expect(binding('amount | currency:"USD$"')).toBe('USD$1,234.56');
expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('USD$1,234.56');
});
it('should update', function() {
input('amount').enter('-1234');
expect(binding('amount | currency')).toBe('($1,234.00)');
expect(binding('amount | currency:"USD$"')).toBe('(USD$1,234.00)');
element(by.model('amount')).clear();
element(by.model('amount')).sendKeys('-1234');
expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
expect(element(by.binding('amount | currency:"USD$"')).getText()).toBe('(USD$1,234.00)');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
currencyFilter.$inject = ['$locale'];
@ -77,25 +78,26 @@ function currencyFilter($locale) {
</script>
<div ng-controller="Ctrl">
Enter number: <input ng-model='val'><br>
Default formatting: {{val | number}}<br>
No fractions: {{val | number:0}}<br>
Negative number: {{-val | number:4}}
Default formatting: <span id='number-default'>{{val | number}}</span><br>
No fractions: <span>{{val | number:0}}</span><br>
Negative number: <span>{{-val | number:4}}</span>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should format numbers', function() {
expect(binding('val | number')).toBe('1,234.568');
expect(binding('val | number:0')).toBe('1,235');
expect(binding('-val | number:4')).toBe('-1,234.5679');
expect(element(by.id('number-default')).getText()).toBe('1,234.568');
expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
});
it('should update', function() {
input('val').enter('3374.333');
expect(binding('val | number')).toBe('3,374.333');
expect(binding('val | number:0')).toBe('3,374');
expect(binding('-val | number:4')).toBe('-3,374.3330');
});
</doc:scenario>
element(by.model('val')).clear();
element(by.model('val')).sendKeys('3374.333');
expect(element(by.id('number-default')).getText()).toBe('3,374.333');
expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
});
</doc:protractor>
</doc:example>
*/
@ -325,22 +327,22 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
<doc:example>
<doc:source>
<span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
{{1288323623006 | date:'medium'}}<br>
<span>{{1288323623006 | date:'medium'}}</span><br>
<span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}<br>
<span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
<span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}<br>
<span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should format date', function() {
expect(binding("1288323623006 | date:'medium'")).
expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).
expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).
expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
dateFilter.$inject = ['$locale'];
@ -439,11 +441,11 @@ function dateFilter($locale) {
<doc:source>
<pre>{{ {'name':'value'} | json }}</pre>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should jsonify filtered objects', function() {
expect(binding("{'name':'value'}")).toMatch(/\{\n "name": ?"value"\n}/);
expect(element(by.binding("{'name':'value'}")).getText()).toMatch(/\{\n "name": ?"value"\n}/);
});
</doc:scenario>
</doc:protractor>
</doc:example>
*
*/

View file

@ -36,28 +36,37 @@
<p>Output letters: {{ letters | limitTo:letterLimit }}</p>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
var numLimitInput = element(by.model('numLimit'));
var letterLimitInput = element(by.model('letterLimit'));
var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
it('should limit the number array to first three items', function() {
expect(element('.doc-example-live input[ng-model=numLimit]').val()).toBe('3');
expect(element('.doc-example-live input[ng-model=letterLimit]').val()).toBe('3');
expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3]');
expect(binding('letters | limitTo:letterLimit')).toEqual('abc');
expect(numLimitInput.getAttribute('value')).toBe('3');
expect(letterLimitInput.getAttribute('value')).toBe('3');
expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
expect(limitedLetters.getText()).toEqual('Output letters: abc');
});
it('should update the output when -3 is entered', function() {
input('numLimit').enter(-3);
input('letterLimit').enter(-3);
expect(binding('numbers | limitTo:numLimit')).toEqual('[7,8,9]');
expect(binding('letters | limitTo:letterLimit')).toEqual('ghi');
numLimitInput.clear();
numLimitInput.sendKeys('-3');
letterLimitInput.clear();
letterLimitInput.sendKeys('-3');
expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
expect(limitedLetters.getText()).toEqual('Output letters: ghi');
});
it('should not exceed the maximum size of input array', function() {
input('numLimit').enter(100);
input('letterLimit').enter(100);
expect(binding('numbers | limitTo:numLimit')).toEqual('[1,2,3,4,5,6,7,8,9]');
expect(binding('letters | limitTo:letterLimit')).toEqual('abcdefghi');
numLimitInput.clear();
numLimitInput.sendKeys('100');
letterLimitInput.clear();
letterLimitInput.sendKeys('100');
expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
function limitToFilter(){

View file

@ -58,29 +58,6 @@
</table>
</div>
</doc:source>
<doc:scenario>
it('should be reverse ordered by aged', function() {
expect(binding('predicate')).toBe('-age');
expect(repeater('table.friend', 'friend in friends').column('friend.age')).
toEqual(['35', '29', '21', '19', '10']);
expect(repeater('table.friend', 'friend in friends').column('friend.name')).
toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']);
});
it('should reorder the table when user selects different predicate', function() {
element('.doc-example-live a:contains("Name")').click();
expect(repeater('table.friend', 'friend in friends').column('friend.name')).
toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']);
expect(repeater('table.friend', 'friend in friends').column('friend.age')).
toEqual(['35', '10', '29', '19', '21']);
element('.doc-example-live a:contains("Phone")').click();
expect(repeater('table.friend', 'friend in friends').column('friend.phone')).
toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']);
expect(repeater('table.friend', 'friend in friends').column('friend.name')).
toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']);
});
</doc:scenario>
</doc:example>
*/
orderByFilter.$inject = ['$parse'];

View file

@ -223,31 +223,14 @@ function $HttpProvider() {
* XMLHttpRequest will transparently follow it, meaning that the error callback will not be
* called for such responses.
*
* # Calling $http from outside AngularJS
* The `$http` service will not actually send the request until the next `$digest()` is
* executed. Normally this is not an issue, since almost all the time your call to `$http` will
* be from within a `$apply()` block.
* If you are calling `$http` from outside Angular, then you should wrap it in a call to
* `$apply` to cause a $digest to occur and also to handle errors in the block correctly.
*
* ```
* $scope.$apply(function() {
* $http(...);
* });
* ```
*
* # Writing Unit Tests that use $http
* When unit testing you are mostly responsible for scheduling the `$digest` cycle. If you do
* not trigger a `$digest` before calling `$httpBackend.flush()` then the request will not have
* been made and `$httpBackend.expect(...)` expectations will fail. The solution is to run the
* code that calls the `$http()` method inside a $apply block as explained in the previous
* section.
* When unit testing (using {@link api/ngMock ngMock}), it is necessary to call
* {@link api/ngMock.$httpBackend#methods_flush $httpBackend.flush()} to flush each pending
* request using trained responses.
*
* ```
* $httpBackend.expectGET(...);
* $scope.$apply(function() {
* $http.get(...);
* });
* $http.get(...);
* $httpBackend.flush();
* ```
*
@ -604,14 +587,14 @@ function $HttpProvider() {
<option>JSONP</option>
</select>
<input type="text" ng-model="url" size="80"/>
<button ng-click="fetch()">fetch</button><br>
<button ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
<button
<button id="fetchbtn" ng-click="fetch()">fetch</button><br>
<button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
<button id="samplejsonpbtn"
ng-click="updateModel('JSONP',
'http://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
Sample JSONP
</button>
<button
<button id="invalidjsonpbtn"
ng-click="updateModel('JSONP', 'http://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
Invalid JSONP
</button>
@ -648,27 +631,34 @@ function $HttpProvider() {
<file name="http-hello.html">
Hello, $http!
</file>
<file name="scenario.js">
<file name="protractorTest.js">
var status = element(by.binding('status'));
var data = element(by.binding('data'));
var fetchBtn = element(by.id('fetchbtn'));
var sampleGetBtn = element(by.id('samplegetbtn'));
var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
it('should make an xhr GET request', function() {
element(':button:contains("Sample GET")').click();
element(':button:contains("fetch")').click();
expect(binding('status')).toBe('200');
expect(binding('data')).toMatch(/Hello, \$http!/);
sampleGetBtn.click();
fetchBtn.click();
expect(status.getText()).toMatch('200');
expect(data.getText()).toMatch(/Hello, \$http!/)
});
it('should make a JSONP request to angularjs.org', function() {
element(':button:contains("Sample JSONP")').click();
element(':button:contains("fetch")').click();
expect(binding('status')).toBe('200');
expect(binding('data')).toMatch(/Super Hero!/);
sampleJsonpBtn.click();
fetchBtn.click();
expect(status.getText()).toMatch('200');
expect(data.getText()).toMatch(/Super Hero!/);
});
it('should make JSONP request to invalid URL and invoke the error handler',
function() {
element(':button:contains("Invalid JSONP")').click();
element(':button:contains("fetch")').click();
expect(binding('status')).toBe('0');
expect(binding('data')).toBe('Request failed');
invalidJsonpBtn.click();
fetchBtn.click();
expect(status.getText()).toMatch('0');
expect(data.getText()).toMatch('Request failed');
});
</file>
</example>

View file

@ -1,13 +1,18 @@
'use strict';
function createXhr(method) {
// IE8 doesn't support PATCH method, but the ActiveX object does
/* global ActiveXObject */
return (msie <= 8 && lowercase(method) === 'patch')
? new ActiveXObject('Microsoft.XMLHTTP')
: new window.XMLHttpRequest();
}
//if IE and the method is not RFC2616 compliant, or if XMLHttpRequest
//is not available, try getting an ActiveXObject. Otherwise, use XMLHttpRequest
//if it is available
if (msie <= 8 && (!method.match(/^(get|post|head|put|delete|options)$/i) ||
!window.XMLHttpRequest)) {
return new window.ActiveXObject("Microsoft.XMLHTTP");
} else if (window.XMLHttpRequest) {
return new window.XMLHttpRequest();
}
throw minErr('$httpBackend')('noxhr', "This browser does not support XMLHttpRequest.");
}
/**
* @ngdoc object
@ -102,7 +107,20 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
}
if (responseType) {
xhr.responseType = responseType;
try {
xhr.responseType = responseType;
} catch (e) {
// WebKit added support for the json responseType value on 09/03/2013
// https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
// known to throw when setting the value "json" as the response type. Other older
// browsers implementing the responseType
//
// The json response type can be ignored if not supported, because JSON payloads are
// parsed on the client-side regardless.
if (responseType !== 'json') {
throw e;
}
}
}
xhr.send(post || null);

View file

@ -31,11 +31,11 @@ var $interpolateMinErr = minErr('$interpolate');
//demo.label//
</div>
</doc:source>
<doc:scenario>
it('should interpolate binding with custom symbols', function() {
expect(binding('demo.label')).toBe('This binding is brought you by // interpolation symbols.');
});
</doc:scenario>
<doc:protractor>
it('should interpolate binding with custom symbols', function() {
expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
});
</doc:protractor>
</doc:example>
*/
function $InterpolateProvider() {

View file

@ -24,7 +24,7 @@ function $IntervalProvider() {
* In tests you can use {@link ngMock.$interval#methods_flush `$interval.flush(millis)`} to
* move forward by `millis` milliseconds and trigger any functions scheduled to run in that
* time.
*
*
* <div class="alert alert-warning">
* **Note**: Intervals created by this service must be explicitly destroyed when you are finished
* with them. In particular they are not automatically destroyed when a controller's scope or a
@ -137,8 +137,8 @@ function $IntervalProvider() {
promise = deferred.promise,
iteration = 0,
skipApply = (isDefined(invokeApply) && !invokeApply);
count = isDefined(count) ? count : 0,
count = isDefined(count) ? count : 0;
promise.then(null, null, fn);

View file

@ -51,7 +51,7 @@ function $LogProvider(){
* @name ng.$logProvider#debugEnabled
* @methodOf ng.$logProvider
* @description
* @param {string=} flag enable or disable debug level messages
* @param {boolean=} flag enable or disable debug level messages
* @returns {*} current value if used as getter or itself (chaining) if used as setter
*/
this.debugEnabled = function(flag) {

View file

@ -707,7 +707,7 @@ Parser.prototype = {
var getter = getterFn(field, this.options, this.text);
return extend(function(scope, locals, self) {
return getter(self || object(scope, locals), locals);
return getter(self || object(scope, locals));
}, {
assign: function(scope, value, locals) {
return setter(object(scope, locals), field, value, parser.text, parser.options);

View file

@ -16,9 +16,9 @@
* asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
*
* <pre>
* // for the purpose of this example let's assume that variables `$q` and `scope` are
* // available in the current lexical scope (they could have been injected or passed in).
*
* // for the purpose of this example let's assume that variables `$q`, `scope` and `okToGreet`
* // are available in the current lexical scope (they could have been injected or passed in).
*
* function asyncGreet(name) {
* var deferred = $q.defer();
*
@ -73,7 +73,7 @@
* constructed via `$q.reject`, the promise will be rejected instead.
* - `reject(reason)` rejects the derived promise with the `reason`. This is equivalent to
* resolving it with a rejection constructed via `$q.reject`.
* - `notify(value)` - provides updates on the status of the promises execution. This may be called
* - `notify(value)` - provides updates on the status of the promise's execution. This may be called
* multiple times before the promise is either resolved or rejected.
*
* **Properties**
@ -223,7 +223,7 @@ function qFactory(nextTick, exceptionHandler) {
reject: function(reason) {
deferred.resolve(reject(reason));
deferred.resolve(createInternalRejectedPromise(reason));
},
@ -380,6 +380,12 @@ function qFactory(nextTick, exceptionHandler) {
* @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
*/
var reject = function(reason) {
var result = defer();
result.reject(reason);
return result.promise;
};
var createInternalRejectedPromise = function(reason) {
return {
then: function(callback, errback) {
var result = defer();

View file

@ -926,7 +926,7 @@ function $RootScopeProvider(){
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
*
* @param {string} name Event name to emit.
* @param {...*} args Optional set of arguments which will be passed onto the event listeners.
* @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
* @return {Object} Event object (see {@link ng.$rootScope.Scope#methods_$on}).
*/
$emit: function(name, args) {
@ -994,7 +994,7 @@ function $RootScopeProvider(){
* onto the {@link ng.$exceptionHandler $exceptionHandler} service.
*
* @param {string} name Event name to broadcast.
* @param {...*} args Optional set of arguments which will be passed onto the event listeners.
* @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
* @return {Object} Event object, see {@link ng.$rootScope.Scope#methods_$on}
*/
$broadcast: function(name, args) {

View file

@ -275,7 +275,7 @@ function $SceDelegateProvider() {
*
* @description
* Returns an object that is trusted by angular for use in specified strict
* contextual escaping contexts (such as ng-html-bind-unsafe, ng-include, any src
* contextual escaping contexts (such as ng-bind-html, ng-include, any src
* attribute interpolation, any dom event binding attribute interpolation
* such as for onclick, etc.) that uses the provided value.
* See {@link ng.$sce $sce} for enabling strict contextual escaping.
@ -502,8 +502,8 @@ function $SceDelegateProvider() {
* It's important to remember that SCE only applies to interpolation expressions.
*
* If your expressions are constant literals, they're automatically trusted and you don't need to
* call `$sce.trustAs` on them. (e.g.
* `<div ng-html-bind-unsafe="'<b>implicitly trusted</b>'"></div>`) just works.
* call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
* `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
*
* Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
* through {@link ng.$sce#methods_getTrusted $sce.getTrusted}. SCE doesn't play a role here.
@ -563,7 +563,7 @@ function $SceDelegateProvider() {
* matched against the **entire** *normalized / absolute URL* of the resource being tested
* (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
* present on the RegExp (such as multiline, global, ignoreCase) are ignored.
* - If you are generating your Javascript from some other templating engine (not
* - If you are generating your JavaScript from some other templating engine (not
* recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
* remember to escape your regular expression (and be aware that you might need more than
* one level of escaping depending on your templating engine and the way you interpolated
@ -580,7 +580,7 @@ function $SceDelegateProvider() {
* ## Show me an example using SCE.
*
* @example
<example module="mySceApp">
<example module="mySceApp" deps="angular-sanitize.js">
<file name="index.html">
<div ng-controller="myAppController as myCtrl">
<i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
@ -624,13 +624,15 @@ function $SceDelegateProvider() {
]
</file>
<file name="scenario.js">
<file name="protractorTest.js">
describe('SCE doc demo', function() {
it('should sanitize untrusted values', function() {
expect(element('.htmlComment').html()).toBe('<span>Is <i>anyone</i> reading this?</span>');
expect(element(by.css('.htmlComment')).getInnerHtml())
.toBe('<span>Is <i>anyone</i> reading this?</span>');
});
it('should NOT sanitize explicitly trusted values', function() {
expect(element('#explicitlyTrustedHtml').html()).toBe(
expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
'<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
'sanitization.&quot;">Hover over this text.</span>');
});
@ -805,8 +807,8 @@ function $SceProvider() {
*
* @description
* Delegates to {@link ng.$sceDelegate#methods_trustAs `$sceDelegate.trustAs`}. As such,
* returns an objectthat is trusted by angular for use in specified strict contextual
* escaping contexts (such as ng-html-bind-unsafe, ng-include, any src attribute
* returns an object that is trusted by angular for use in specified strict contextual
* escaping contexts (such as ng-bind-html, ng-include, any src attribute
* interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
* that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
* escaping.

View file

@ -31,13 +31,13 @@
<button ng-click="doGreeting(greeting)">ALERT</button>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should display the greeting in the input box', function() {
input('greeting').enter('Hello, E2E Tests');
element(by.model('greeting')).sendKeys('Hello, E2E Tests');
// If we click the button it will block the test runner
// element(':button').click();
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
function $WindowProvider(){

View file

@ -828,7 +828,10 @@ angular.module('ngAnimate', ['ng'])
}
function fireDoneCallbackAsync() {
doneCallback && async(doneCallback);
async(function() {
fireDOMCallback('close');
doneCallback && doneCallback();
});
}
//it is less complicated to use a flag than managing and cancelling

View file

@ -379,7 +379,7 @@ angular.mock.$LogProvider = function() {
*
* @example
* <pre>
* $log.log('Some Error');
* $log.error('Some Error');
* var first = $log.error.logs.unshift();
* </pre>
*/
@ -504,6 +504,7 @@ angular.mock.$IntervalProvider = function() {
};
$interval.cancel = function(promise) {
if(!promise) return false;
var fnIndex;
angular.forEach(repeatFns, function(fn, index) {
@ -989,18 +990,18 @@ angular.mock.dump = function(object) {
*
* # Flushing HTTP requests
*
* The $httpBackend used in production, always responds to requests with responses asynchronously.
* If we preserved this behavior in unit testing, we'd have to create async unit tests, which are
* hard to write, follow and maintain. At the same time the testing mock, can't respond
* The $httpBackend used in production always responds to requests with responses asynchronously.
* If we preserved this behavior in unit testing we'd have to create async unit tests, which are
* hard to write, understand, and maintain. However, the testing mock can't respond
* synchronously because that would change the execution of the code under test. For this reason the
* mock $httpBackend has a `flush()` method, which allows the test to explicitly flush pending
* requests and thus preserving the async api of the backend, while allowing the test to execute
* requests and thus preserve the async api of the backend while allowing the test to execute
* synchronously.
*
*
* # Unit testing with mock $httpBackend
* The following code shows how to setup and use the mock backend in unit testing a controller.
* First we create the controller under test
* The following code shows how to setup and use the mock backend when unit testing a controller.
* First we create the controller under test:
*
<pre>
// The controller code
@ -1025,7 +1026,7 @@ angular.mock.dump = function(object) {
}
</pre>
*
* Now we setup the mock backend and create the test specs.
* Now we setup the mock backend and create the test specs:
*
<pre>
// testing controller
@ -1729,7 +1730,7 @@ angular.mock.$RootElementProvider = function() {
* In addition, ngMock also extends various core ng services such that they can be
* inspected and controlled in a synchronous manner within test code.
*
* {@installModule mocks}
* {@installModule mock}
*
* <div doc-module-components="ngMock"></div>
*
@ -1947,7 +1948,7 @@ if(window.jasmine || window.mocha) {
var currentSpec = null,
isSpecRunning = function() {
return currentSpec && (window.mocha || currentSpec.queue.running);
return !!currentSpec;
};
@ -2138,9 +2139,8 @@ if(window.jasmine || window.mocha) {
}
for(var i = 0, ii = blockFns.length; i < ii; i++) {
try {
/* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */
injector.invoke(blockFns[i] || angular.noop, this);
/* jshint +W040 */
// jasmine sets this to be the current spec, so we are mimicing that
injector.invoke(blockFns[i] || angular.noop, currentSpec);
} catch (e) {
if (e.stack && errorForStack) {
throw new ErrorAddingDeclarationLocationStack(e, errorForStack);

View file

@ -35,7 +35,7 @@ function shallowClearAndCopy(src, dst) {
});
for (var key in src) {
if (src.hasOwnProperty(key) && key.charAt(0) !== '$' && key.charAt(1) !== '$') {
if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
dst[key] = src[key];
}
}
@ -387,7 +387,9 @@ angular.module('ngResource', ['ng']).
val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
if (angular.isDefined(val) && val !== null) {
encodedVal = encodeUriSegment(val);
url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), encodedVal + "$1");
url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
return encodedVal + p1;
});
} else {
url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
leadingSlashes, tail) {

View file

@ -151,16 +151,17 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
}
</file>
<file name="scenario.js">
<file name="protractorTest.js">
it('should load and compile correct template', function() {
element('a:contains("Moby: Ch1")').click();
var content = element('.doc-example-live [ng-view]').text();
element(by.linkText('Moby: Ch1')).click();
var content = element(by.css('.doc-example-live [ng-view]')).getText();
expect(content).toMatch(/controller\: ChapterCntl/);
expect(content).toMatch(/Book Id\: Moby/);
expect(content).toMatch(/Chapter Id\: 1/);
element('a:contains("Scarlet")').click();
content = element('.doc-example-live [ng-view]').text();
element(by.partialLinkText('Scarlet')).click();
content = element(by.css('.doc-example-live [ng-view]')).getText();
expect(content).toMatch(/controller\: BookCntl/);
expect(content).toMatch(/Book Id\: Scarlet/);
});

View file

@ -185,7 +185,7 @@ function $RouteProvider(){
path = path
.replace(/([().])/g, '\\$1')
.replace(/(\/)?:(\w+)([\?|\*])?/g, function(_, slash, key, option){
.replace(/(\/)?:(\w+)([\?\*])?/g, function(_, slash, key, option){
var optional = option === '?' ? option : null;
var star = option === '*' ? option : null;
keys.push({ name: key, optional: !!optional });
@ -345,17 +345,17 @@ function $RouteProvider(){
}
</file>
<file name="scenario.js">
<file name="protractorTest.js">
it('should load and compile correct template', function() {
element('a:contains("Moby: Ch1")').click();
var content = element('.doc-example-live [ng-view]').text();
element(by.linkText('Moby: Ch1')).click();
var content = element(by.css('.doc-example-live [ng-view]')).getText();
expect(content).toMatch(/controller\: ChapterCntl/);
expect(content).toMatch(/Book Id\: Moby/);
expect(content).toMatch(/Chapter Id\: 1/);
element('a:contains("Scarlet")').click();
sleep(2); // promises are not part of scenario waiting
content = element('.doc-example-live [ng-view]').text();
element(by.partialLinkText('Scarlet')).click();
content = element(by.css('.doc-example-live [ng-view]')).getText();
expect(content).toMatch(/controller\: BookCntl/);
expect(content).toMatch(/Book Id\: Scarlet/);
});
@ -370,7 +370,7 @@ function $RouteProvider(){
* @eventType broadcast on root scope
* @description
* Broadcasted before a route change. At this point the route services starts
* resolving all of the dependencies needed for the route change to occurs.
* resolving all of the dependencies needed for the route change to occur.
* Typically this involves fetching the view template as well as any dependencies
* defined in `resolve` route property. Once all of the dependencies are resolved
* `$routeChangeSuccess` is fired.

View file

@ -67,37 +67,38 @@
</tr>
</table>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should linkify the snippet with urls', function() {
expect(using('#linky-filter').binding('snippet | linky')).
toBe('Pretty text with some links:&#10;' +
'<a href="http://angularjs.org/">http://angularjs.org/</a>,&#10;' +
'<a href="mailto:us@somewhere.org">us@somewhere.org</a>,&#10;' +
'<a href="mailto:another@somewhere.org">another@somewhere.org</a>,&#10;' +
'and one more: <a href="ftp://127.0.0.1/">ftp://127.0.0.1/</a>.');
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
});
it ('should not linkify snippet without the linky filter', function() {
expect(using('#escaped-html').binding('snippet')).
toBe("Pretty text with some links:\n" +
"http://angularjs.org/,\n" +
"mailto:us@somewhere.org,\n" +
"another@somewhere.org,\n" +
"and one more: ftp://127.0.0.1/.");
it('should not linkify snippet without the linky filter', function() {
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +
'another@somewhere.org, and one more: ftp://127.0.0.1/.');
expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
});
it('should update', function() {
input('snippet').enter('new http://link.');
expect(using('#linky-filter').binding('snippet | linky')).
toBe('new <a href="http://link">http://link</a>.');
expect(using('#escaped-html').binding('snippet')).toBe('new http://link.');
element(by.model('snippet')).clear();
element(by.model('snippet')).sendKeys('new http://link.');
expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
toBe('new http://link.');
expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
.toBe('new http://link.');
});
it('should work with the target property', function() {
expect(using('#linky-target').binding("snippetWithTarget | linky:'_blank'")).
toBe('<a target="_blank" href="http://angularjs.org/">http://angularjs.org/</a>');
expect(element(by.id('linky-target')).
element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
toBe('http://angularjs.org/');
expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {

View file

@ -99,35 +99,37 @@ var $sanitizeMinErr = angular.$$minErr('$sanitize');
</table>
</div>
</doc:source>
<doc:scenario>
<doc:protractor>
it('should sanitize the html snippet by default', function() {
expect(using('#bind-html-with-sanitize').element('div').html()).
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
});
it('should inline raw snippet if bound to a trusted value', function() {
expect(using('#bind-html-with-trust').element("div").html()).
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).
toBe("<p style=\"color:blue\">an html\n" +
"<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
"snippet</p>");
});
it('should escape snippet without any filter', function() {
expect(using('#bind-default').element('div').html()).
expect(element(by.css('#bind-default div')).getInnerHtml()).
toBe("&lt;p style=\"color:blue\"&gt;an html\n" +
"&lt;em onmouseover=\"this.textContent='PWN3D!'\"&gt;click here&lt;/em&gt;\n" +
"snippet&lt;/p&gt;");
});
it('should update', function() {
input('snippet').enter('new <b onclick="alert(1)">text</b>');
expect(using('#bind-html-with-sanitize').element('div').html()).toBe('new <b>text</b>');
expect(using('#bind-html-with-trust').element('div').html()).toBe(
element(by.model('snippet')).clear();
element(by.model('snippet')).sendKeys('new <b onclick="alert(1)">text</b>');
expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
toBe('new <b>text</b>');
expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe(
'new <b onclick="alert(1)">text</b>');
expect(using('#bind-default').element('div').html()).toBe(
expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
"new &lt;b onclick=\"alert(1)\"&gt;text&lt;/b&gt;");
});
</doc:scenario>
</doc:protractor>
</doc:example>
*/
function $SanitizeProvider() {

View file

@ -190,7 +190,7 @@ describe('angular', function() {
expect(copy.key).toBe(original.key);
});
it('should not copy $$ properties nor prototype properties', function() {
it('should omit "$$"-prefixed properties', function() {
var original = {$$some: true, $$: true};
var clone = {};
@ -198,6 +198,27 @@ describe('angular', function() {
expect(clone.$$some).toBeUndefined();
expect(clone.$$).toBeUndefined();
});
it('should copy "$"-prefixed properties from copy', function() {
var original = {$some: true};
var clone = {};
expect(shallowCopy(original, clone)).toBe(clone);
expect(clone.$some).toBe(original.$some);
});
it('should omit properties from prototype chain', function() {
var original, clone = {};
function Func() {};
Func.prototype.hello = "world";
original = new Func();
original.goodbye = "world";
expect(shallowCopy(original, clone)).toBe(clone);
expect(clone.hello).toBeUndefined();
expect(clone.goodbye).toBe("world");
});
});
describe('elementHTML', function() {

View file

@ -65,6 +65,17 @@ describe('jqLite', function() {
});
it('should allow construction of html with leading whitespace', function() {
var nodes = jqLite(' \n\r \r\n<div>1</div><span>2</span>');
expect(nodes[0].parentNode).toBeDefined();
expect(nodes[0].parentNode.nodeType).toBe(11); /** Document Fragment **/;
expect(nodes[0].parentNode).toBe(nodes[1].parentNode);
expect(nodes.length).toBe(2);
expect(nodes[0].innerHTML).toBe('1');
expect(nodes[1].innerHTML).toBe('2');
});
it('should allow creation of comment tags', function() {
var nodes = jqLite('<!-- foo -->');
expect(nodes.length).toBe(1);

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