Compare commits

..

103 Commits

Author SHA1 Message Date
marcysutton 1f5e42e882 feat(ngAria): add option to disable role=button
Closes #11580
Closes #12234
2015-07-02 14:36:49 +03:00
Jeff Cross d193c3a25c revert: "fix($compile): do not write @-bound properties if attribute is not present"
This reverts commit 8a1eb1625c.

This commit broke the tabs component on the material project,
which caused internal breakages. Will open a separate issue to
look into the issue.
2015-07-01 22:12:03 -07:00
Raphael Jamet 4da1cc3b81 refactor($templateRequest): Remove useless dependencies in tests 2015-07-01 12:15:00 -07:00
Raphael Jamet 6de08216e7 docs($templateRequest): update the description with caching changes
The previous changes to $templateRequest were not documented, they now are.
2015-07-01 12:15:00 -07:00
Raphael Jamet 3c6e8ce044 refactor($templateRequest): move $sce checks and trust the cache
Move all the calls to $sce.getTrustedUrl inside $templateRequest, and
also trust the contents of the cache. This allows prefetching templates
and to bypass the checks on things where they make no sense, like
templates specified in script tags.

Closes #12219
Closes #12220
Closes #12240
2015-07-01 12:14:42 -07:00
Peter Bacon Darwin e51024ed54 docs(ngAnimate): fix typos 2015-06-30 13:19:52 +01:00
Chris Bosco 04f1ebd470 docs(guide/Migrating): $animate.on/off example updated to match docs
Closes #12212
2015-06-30 03:05:05 +01:00
Bart Verkoeijen 2e63ab734a docs(guide/Migrating): add breaking change for 1.4 regarding controller constructors.
If you previously returned a object from a controller constructor function,
it would not be bound to the scope. As of 1.4 it does, and can cause
unexpected objects as the scope.

Closes #12227
2015-06-30 02:59:46 +01:00
Georgios Kalpakas 6333d65b76 fix($compile): throw error when requestng new and isolate scopes (async)
While directives are not allowed to request both a new (normal) and an
isolate scope on the same element, the relevant check was not performed
correctly when the directive requesting the isolate scope had a lower
priority and specified a `templateUrl`.
In that case, the check was deferred until the template was fetched, but
the info about other directives requesting a new (normal) scope was not
properly preserved (in `previousCompileContext`).

This commit fixes this, so now an error is thrown (as expected).

Fixes #12215
Closes #12217
2015-06-28 11:33:06 +03:00
Yichao Wang 1ce5d216c7 docs(ngAnimate): fix typos in JS and CSS selector
Closes #12203
2015-06-25 12:42:17 +03:00
Georgios Kalpakas 28c166939e test($compile): test default value for optional attribute with new scope
Related to #12151
Closes #12194
2015-06-24 10:25:55 +02:00
Peter Bacon Darwin 14638f4a60 fix(ngOptions): only watch numeric properties of an array
It turns out that the options that are displayed are more constrained than
just whether the property starts with a $ character.
If the values collection is array-like then we only display the options that
are identified by numerical properties - it's an array right?

So this commit aligns `getWatchables` with `getOptions`.

See #12010
2015-06-23 17:47:34 -07:00
Peter Bacon Darwin 0c1fbdd242 chore(doc-gen): update to dgeni-packages 0.10.17
Make proper use of the new `git` package in dgeni-packages
2015-06-23 04:41:45 -07:00
Martin Staffa 33f7f26558 docs(ngChecked): note that it shouldn't be used with ngModel
Closes #11106
2015-06-22 23:39:08 +02:00
Martin Staffa e27eed3ca4 docs(ngCookies): improve deprecation notice 2015-06-22 23:37:50 +02:00
Gabriel Monteagudo 6cbbd96647 fix(merge): treat dates as atomic values instead of objects.
Makes angular.merge copy dates correctly.

Closes #11720

Closes #11720
2015-06-22 21:21:09 +02:00
Phil Brown d0cb69348e docs($http): set correct link to XMLHttpRequest.responseType
The link to MDN XMLHttpRequest.responseType was incorrect.

Closes #12183
2015-06-22 18:51:46 +02:00
Jandalf f15f8df2b4 docs($httpProvider): typo fix
fix a typo:
default.cache -> defaults.cache
2015-06-21 14:07:13 +02:00
Peter Bacon Darwin e7662ebc31 docs(directives): add multiElement tag to appropriate directives
Closes #11104
2015-06-21 01:52:01 +01:00
Peter Bacon Darwin 8ceed4faf3 chore(dependencies): update to dgeni-packages 0.10.15
Closes #11104
Closes #11418
2015-06-21 01:51:31 +01:00
Tsuyoshi Yoshizawa 6903b5ec4c fix($location): allow navigating outside the original base URL
Previously, if you navigated outside of the current base URL angular
crashed with a `Cannot call method 'charAt' of undefined` error.

Closes #11302
Closes #4776
2015-06-19 18:40:28 +01:00
Peter Bacon Darwin 48e1f5605e fix(orderBy): ensure correct ordering with arrays of objects and no predicate
By refactoring to use a Schwartzian transform, we can ensure that objects
with no custom `toString` or `toValue` methods are just ordered using
their position in the original collection.

Closes #11866
Closes #11312
Closes #4282
2015-06-18 08:36:16 +01:00
Peter Bacon Darwin c5a3d8fc5f refact(filter): move hasCustomToString into shared namespace to be reused 2015-06-18 08:33:28 +01:00
Martin Staffa c61149213b docs(CHANGELOG): fix position for some entries in 1.4.1 2015-06-17 23:57:54 +02:00
Stéphane Campinas ad7200e2c2 docs(tutorial/Tutorial): add missing word
Closes #12147
2015-06-17 21:19:02 +02:00
Alfonso Presa bea74c0f56 docs(ngTouch): Document event parameter for $swipe handlers
Document that the event handlers provided by $swipe do receive the raw
event as their last parameter.

Closes #11983

Closes #12149
2015-06-17 21:17:33 +02:00
Martin Staffa bacc3b7e0e docs(tutorial): make docTutorialReset use a button
It's confusing to have it look like a link that goes to the homepage

Closes #1473
2015-06-17 21:14:39 +02:00
Caitlin Potter 8a1eb1625c fix($compile): do not write @-bound properties if attribute is not present
Shadows only when attributes are non-optional and not own properties,
only stores the observed '@' values when they are indeed strings.

Partial revert of 6339d30d1379689da5eec9647a953f64821f8b0

Closes #12151
Closes #12144
2015-06-17 17:40:12 +01:00
Peter Bacon Darwin ed27e0ea6a docs(CHANGELOG): fix typo 2015-06-17 14:35:25 +01:00
Peter Bacon Darwin f81ff3beb0 fix($browser): prevent infinite digest if changing hash when there is no hashPrefix
The `window.location.hash' setter will strip of a single leading hash character
if it exists from the fragment  before joining the fragment value to the href
with a new hash character.

This meant that if we want the fragment to lead with a hash character, we
must do `window.location.hash = '##test'` to ensure that the first hash
character in the fragment is not lost.

The `$location.hash` setter works differently and assumes that the value
passed is the the full fragment, i.e. it does not attempt to strip off a
single leading hash character.

Previously, if you called, `$location.hash('#test')`, the leading hash was
being stripped and the resulting url fragment did not contain this hash:
`$location.hash()`, then became 'test' rather than `#test`, which led to
an infinite digest.

Closes #10423
Closes #12145
2015-06-17 14:01:20 +01:00
Martin Staffa 720012eab6 docs(resource): clarify success callback arguments
Closes #7730
2015-06-16 19:50:42 +02:00
Kent C. Dodds 3adfe5bda9 docs(guide/Unit Testing): add variable declaration to $filter test
The $filter example never declares `$filter` and therefore would add
`$filter` to the global namespace. Add variable declaration.

Closes #12129
2015-06-16 19:12:10 +02:00
Martin Staffa dc0467879d docs(input[radio]): clarify difference between value and ngValue
Closes #7971
2015-06-16 19:11:11 +02:00
Peter Bacon Darwin 528d7f9568 docs(CHANGELOG): add 1.4.1 changes 2015-06-16 14:52:17 +01:00
Georgios Kalpakas 636ce70e47 style(forms): fix indentation in example 2015-06-16 15:34:02 +03:00
Nir Noy 5f5ee0f880 docs(forms): remove redundant call to $scope.$apply()
As of Angular 1.3 `$setViewValue` already calls `$apply` and triggers a
digest cycle, so now there is no need wrapping the `$setViewValue`
function call with `$apply`, which will just trigger an additional digest
cycle.
2015-06-16 13:20:57 +03:00
Thomas Landauer 46b7cf7464 docs(filter): document third argument of predicate function 2015-06-15 20:50:59 +02:00
Martin Staffa 860edee65b docs(changelog): fix typo
Closes #12085
2015-06-15 20:45:28 +02:00
Caitlin Potter ebd0fbba8f fix(forms): parse exponential notation in numberInputType parser
Support parsing numbers in exponential notation, which Number.prototype.toString() returns
for sufficiently high numbers.

Closes #12121
Closes #12122
2015-06-15 08:46:47 -04:00
Rouven Weßling 093416f60f chore(npm): add the license to package.json
This removes a warning when executing npm install.

Closes #12111
2015-06-15 14:17:56 +03:00
Martin Staffa 0400dc9c2a docs($http): expand the param serializer docs
Closes #11745
Closes #12064
2015-06-14 13:36:37 +02:00
Lucas Galfaso 71fc3f4fa0 fix($parse): set null reference properties to undefined
When there is an expression of the form
* true && a.b.c
* true && a()
* true && a()()
* false || a.b.c
* false || a()
* false || a()()

where `a == null`

Closes #12099
2015-06-13 12:38:57 +02:00
Martin Staffa 8caf1802e0 fix(compile): assign ctrl return values correctly for multiple directives
Fixes #12029
Closes #12036
2015-06-12 21:41:55 +02:00
Peter Bacon Darwin 91b602263b fix($location): do not get caught in infinite digest in IE9
Thanks to @hamfastgamgee for getting this fix in place.

Closes #11439
Closes #11675
Closes #11935
Closes #12083
2015-06-12 14:35:46 +01:00
Lucas Galfaso 0934b76b72 fix(ngModel): form validation when there is an Object.prototype enumerable value
When adding an Object.prototype enumerable property, this should not be confused
as a form error

Closes #12066
2015-06-11 23:25:08 +02:00
Caitlin Potter 571bee7b2f test($location): ensure mock window can be wrapped by jqLite
Fixin da build

Closes #12086
2015-06-11 12:04:56 -04:00
Peter Bacon Darwin 11055132bf test($locationSpec): refactor and clean up tests 2015-06-11 14:29:13 +01:00
Peter Bacon Darwin 0898b1240b test($logSpec): don't pollute the global namespace with helpers 2015-06-11 13:29:15 +01:00
Conny Sjöblom 8dc09e6dab fix(linky): allow case insensitive scheme detection
Closes #12073
Closes #12073
2015-06-10 14:38:55 +01:00
Lucas Galfaso 799353c75d fix($sanitize): dont not remove tab index property
Closes #8371
Closes #5853
2015-06-10 11:48:00 +02:00
David Link ffac747e84 docs(tutorial/Tutorial): clarify what npm install does
If the reader does not cd into angular-phonecat subdirectory,
npm install will not work.
This comment helps newbies understand npm install is dependent
on a custom project.json file.

Closes #12040
2015-06-09 21:41:12 +02:00
Martin Staffa 998340de7f docs(select): correct workaround for numeric option bc 2015-06-09 21:35:59 +02:00
Georgios Kalpakas 559313652e test($compile): fix test
The fixed test is supposed to test a fix for an IE11 bug/peculiarity that
arises when using a specifically configured MutationObserver on the page
(see #11781 for more info).
The configuration contained a typo (`sublist` instead of `subtree`), which
effectively failed to set up the MutationObserver in a way that would make
the IE11 bug appear.

Closes #12061
2015-06-09 18:29:26 +03:00
Martin Staffa a69251ab55 docs(select): add note about breaking change with numeric values
Closes #12052
2015-06-08 21:15:14 +02:00
David Anderton 7e5248a33f docs(orderBy): correctly is not the right word
The use of correctly implies that Angular is doing something incorrect, however it is that we expect a number passed as a string to be sorted as a number. Angular does not do what we are expecting, although it responds correctly to what we have actually asked - sorting based on the string representation of a number.

Closes #12046
2015-06-08 21:15:10 +02:00
Martin Staffa c210ff5eae docs($compile): correct what gets passed to ctrl argument
Closes #11903
2015-06-08 21:15:07 +02:00
Jason Bedard 9efb0d5ee9 perf($compile): avoid jquery data calls when there is no data 2015-06-08 12:16:09 +02:00
Jason Bedard 0e622f7b5b fix(copy): do not copy the same object twice 2015-06-08 12:12:56 +02:00
Martin Staffa 071be60927 docs($animateCss): fix a dangling link 2015-06-06 14:45:01 +02:00
Martin Staffa a25aa5b577 docs(input[number]): mention incompatibility with allowInvalid
Closes #11390
2015-06-06 14:44:56 +02:00
Josh Sherick df9c720d08 docs(tutorial/step0): display list correctly
Two bullet points were indented causing the entire lines
to be formatted as code by markdown.
Decreased indentation level by one, as it seems this was not the intention.

Closes #12035
2015-06-06 14:44:52 +02:00
Will Fong 4fe141f794 docs(tutorial/index): add a "next step" sentence
All the other pages have a "continue to next page" link
at the bottom of the page. Keeps a nice reading flow.

Closes #12038
2015-06-06 14:44:28 +02:00
Lucas Galfaso f3b1d0b723 fix($compile): workaround for IE11 MutationObserver
IE11 MutationObserver breaks consecutive text nodes into several text nodes.
This patch merges consecutive text nodes into a single node before
looking for interpolations.

Closes #11781
2015-06-06 11:26:40 +02:00
qbzzt 288225b080 docs(ngClass): add class 'has-error' to demonstrate hyphen use
Modify the example, to show that, when using `ngClass`'s map syntax,
hyphenated classes (e.h. such as those used by Bootstrap) must be enclosed
in quotes.

Closes #12027
2015-06-06 01:34:31 +03:00
Matias Niemelä e967abcd30 docs(CHANGELOG): add changes for 1.3.16 2015-06-05 13:40:38 -07:00
Igor Minar 2c3cd8126c docs(http): add info about unique cookies and XSRF protection on shared domains
Closes #12028
2015-06-04 22:15:55 -07:00
Martin Staffa 41385f0afc docs(*): improve accessibility of links
Closes #7932
2015-06-04 23:00:43 +02:00
Martin Staffa 500b0f6cdb docs(guide/expressions): include filters in one-time binding examples
Closes #8776
2015-06-04 23:00:40 +02:00
Allan Bogh 40b27280ab docs(ngAria): clarify which module to include for ngAria
Closes #11802
2015-06-04 23:00:37 +02:00
Kevin Huang b73c64e2fb docs(guide/Conceptual Overview): remove text from in-page anchor tags
The in-page anchor tags that serve as bookmarks for information about views and models have
visible text content that unintentionally makes them seem like clickable links navigating to
other parts of the page or to entirely different pages.

Closes #11450
2015-06-04 23:00:34 +02:00
fahdsheikh 2f5d42ec72 docs(changelog): fixed grammatical error
Changed "a animation" to "an animation" for grammatical consistency

Closes #11672
2015-06-04 23:00:31 +02:00
Georgios Kalpakas 25d731e9b0 docs(orderBy): fix JSCS trailing whitespace error 2015-06-04 10:55:07 +03:00
Robert Jenks 15da7cc3dc docs(orderBy): improve sorting behaviour and move logic into the controller
Improve the sorting behaviour in the 2nd example: Clicking on an unsorted
column sorts in ascending order, while clicking on a sorted column sorts
in descending order. Also, add a simple sort indicator.

Closes #11981
2015-06-04 10:11:16 +03:00
Peter Bacon Darwin 34a6da24c1 fix(ngOptions): do not watch properties starting with $
Expressions that compute labels and track by values for ngOptions were
being called for properties, that start with $ even though those properties
were being ignored as options.

Closes #11930
Closes #12010
2015-06-04 06:28:00 +01:00
Georgios Kalpakas ebaa0f5985 fix(ngAria): update aria-valuemin/max when min/max change
As a result of thi fix, `ngMin/Max` also set `aria-valuemin/max` on
"range"-shaped elements.

Fixes #11770

Closes #11774
2015-06-03 21:23:38 +02:00
Dominick bb15d414c6 docs(guide/Modules): simple grammar fix
the module consist of… --> the module consists of…

Closes #11843
2015-06-03 21:07:46 +02:00
Dominick f056036a4a docs(guide/security): remove errant word
Closes #11870
2015-06-03 21:06:44 +02:00
Dominick a055762027 docs(guide/i18n): grammar fix
contraction "it's" --> possessive "its"
2015-06-03 21:06:19 +02:00
Dominick 82d38e4453 docs(guide/Using $location): format parameter name
Format parameter name as code. (It's used elsewhere in this doc page in code examples, as code itself.)
2015-06-03 21:04:13 +02:00
Steve Mao 6a743f0b5b docs(ngModelOptions): make object notation style consistent
There is a space before and after `{` and `}` for most of the objects throughout the docs.

Closes #11871
2015-06-03 21:03:16 +02:00
shoja 31f6f76291 docs($httpBackend): correct typo
docs($httpBackend): correct typo

Correct "send" to "sent".

Closes #11876
2015-06-03 21:02:50 +02:00
David Eriksson a2b5a5ed5f docs($cookiesProvider): escape HTML
Escape the "base" HTML element so it will be displayed in the online documentation.

Closes #11897
2015-06-03 21:01:47 +02:00
Boris Cherny 1d41e8a975 docs(ngRepeat): document that track by must be the last expression
this is a point of confusion that's not well documented. see #5520

Closes #11934
2015-06-03 20:57:56 +02:00
Pascal Precht 01182dfbb8 docs(ngMessages): fixes logical bug
The paragraph below this changes says:

"Then the `required` message will be displayed first."

`required` needs to be `true` to match that sentence.

Closes #12009
2015-06-03 19:59:26 +02:00
Yuriy Bash e17f85cc5b docs(ngMessages): fix spelling error
Closes #12019
2015-06-03 19:56:51 +02:00
Steven Easter d7dc14dc0c fix(ngOptions): use reference check only when not using trackBy
Change the check on changed value for ngOptions to ensure that a reference check is only done
when trackBy is not used.  Previously, if trackBy was being used but the values of the objects were
equal a reference check was then used which would cause the check to be true if the objects had
different memory references but the values were equal.  This can cause issues with forms being
marked dirty when the value of the model as not actually changed.

Closes #11936
Closes #11996
2015-06-02 12:37:33 +01:00
Henry Zhu 578fa019b3 chore(jscs): remove .jscs.json.todo, rename config to .jscsrc
Closes #11993
2015-06-02 10:29:54 +01:00
Michal Miszczyszyn 3c9096efb4 test(ngAria): test that aria-hidden/disabled are always "true" or "false"
Previously, when using ngAria with the ng-hide directive,
and the value passed to ng-hide was not boolean,
the aria-hidden attribute was set to this non-boolean value.

Closes #11865
Closes #11998
2015-06-01 23:13:22 +02:00
Fred Sauer 59273354b5 fix(ngAria): ensure boolean values for aria-hidden and aria-disabled
aria-hidden should mirror the boolean representation of their ng-*
counterpart (ng-show, ng-hide) instead of their actual value. Same
applies to aria-disabled and ng-disabled

Closes #11365
2015-06-01 23:13:21 +02:00
Xavier Haniquaut f67204794e docs(migration): fix minor typo
Closes #11994
2015-06-01 21:51:47 +01:00
Wenhua Zhao b6389eedda chore(injector): avoid invoking noop 2015-06-01 11:48:36 +02:00
Ran Ding a6339d30d1 fix($compile): exception when using "watch" as isolated scope binding variable in Firefox
Fix on all binding modes: '=', '@' and '&' as well as optional cases
Throw exception when user define 'hasOwnProperty' in binding.

Closes #11627
2015-06-01 11:36:18 +02:00
Vladimir Lugovsky 351fe4b79c feat($compile): show module name during multidir error
Show module name if possible when multidir error happens.

Closes #11775
2015-06-01 11:08:27 +02:00
Lucas Galfaso d19504a179 fix($parse): set null reference properties to undefined
When there is an expression of the form `true && a.b` and where `a == null`, then set
the value of `true && a.b` to `undefined`.

Closes #11959
2015-06-01 11:03:12 +02:00
Jeff Lee e5e871fe1a docs(ngAnimate): fix typo
Closes #11957
2015-06-01 08:03:20 +01:00
Ron Tsui 90fa884f94 docs(README): improve unusual phrasing
Closes #11958
2015-06-01 08:01:26 +01:00
Yi EungJun 80b9018f29 docs(guide/Directives): use more standard data-ng-model in example
Use data-ng-model instead of data-ng:model which is accepted for legacy reason.
The next "Normalization" section is saying:

> Best Practice: Prefer using the dash-delimited format (e.g. ng-bind for
> ngBind). If you want to use an HTML validating tool, you can instead use the
> data-prefixed version (e.g. data-ng-bind for ngBind). The other forms
> shown above are accepted for legacy reasons but we advise you to avoid
> them.

Closes #11960
2015-06-01 07:59:05 +01:00
Daniel f6ac226c8b docs(numberFilter): update to match handling of null and undefined
This changed in 2ae10f67fc, where null and
undefined are passed through.

Closes #11963
2015-06-01 07:55:37 +01:00
Michael Watts 2d22380873 docs(guide/Scopes): capitalisation of word scope
Closes #11970
2015-06-01 07:50:12 +01:00
Peter Bacon Darwin 7c49d9986f docs(README.closure.md): clarify sentence
Closes #11979
2015-06-01 07:48:21 +01:00
pholly 209f4f3e0f docs(guide/Expressions): added special case for one-time binding of object literals under Value stabilization algorithm
One time binding of object literals are treated differently than simple expressions. Added a link to Ben Nadel's article describing how object literals's keys are checked for undefined.

Closes #11982
2015-06-01 07:42:22 +01:00
Tero Parviainen 5d68c763e2 refactor($compile): remove unused elementTransclusion argument
Remove the unused elementTransclusion argument from createBoundTranscludeFn.
Also remove the nodeLinkFn.elementTranscludeOnThisElement attribute, which
becomes unnecessary.

Closes #9962
Closes #11985
2015-06-01 07:36:36 +01:00
Ryan Dale 3ef529806f feat($q): $q.resolve as an alias for $q.when
New "when" alias "resolve" to maintain naming consistency with ES6.

Closes #11944
Closes #11987
2015-06-01 07:31:51 +01:00
77 changed files with 2741 additions and 1316 deletions
-15
View File
@@ -1,15 +0,0 @@
// 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.
{
"validateParameterSeparator": ", ", // Re-assert this rule when JSCS allows multiple spaces
"requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"],
"disallowImplicitTypeConversion": ["string"],
"disallowMultipleLineBreaks": true,
"validateJSDoc": {
"checkParamNames": true,
"requireParamTypes": true
}
}
View File
+155 -2
View File
@@ -1,3 +1,130 @@
<a name="1.4.1"></a>
# 1.4.1 hyperionic-illumination (2015-06-16)
## Bug Fixes
- **$compile:**
- workaround for IE11 MutationObserver
([f3b1d0b7](https://github.com/angular/angular.js/commit/f3b1d0b723298a5f8ea21d0704405649cce1b5fc),
[#11781](https://github.com/angular/angular.js/issues/11781))
- prevent exception when using `watch` as isolated scope binding property in Firefox
([a6339d30](https://github.com/angular/angular.js/commit/a6339d30d1379689da5eec9647a953f64821f8b0),
[#11627](https://github.com/angular/angular.js/issues/11627))
- assign controller return values correctly for multiple directives
([8caf1802](https://github.com/angular/angular.js/commit/8caf1802e0e93389dec626ef35e04a302aa6c39d),
[#12029](https://github.com/angular/angular.js/issues/12029), [#12036](https://github.com/angular/angular.js/issues/12036))
- **$location:** do not get caught in infinite digest in IE9 when redirecting in `$locationChangeSuccess`
([91b60226](https://github.com/angular/angular.js/commit/91b602263b96b6fce1331208462e18eb647f4d60),
[#11439](https://github.com/angular/angular.js/issues/11439), [#11675](https://github.com/angular/angular.js/issues/11675), [#11935](https://github.com/angular/angular.js/issues/11935), [#12083](https://github.com/angular/angular.js/issues/12083))
- **$parse:** set null reference properties to `undefined`
([71fc3f4f](https://github.com/angular/angular.js/commit/71fc3f4fa0cd12eff335d57efed7c033554749f4),
[#12099](https://github.com/angular/angular.js/issues/12099))
([d19504a1](https://github.com/angular/angular.js/commit/d19504a179355d7801d59a8db0285a1322e04601),
[#11959](https://github.com/angular/angular.js/issues/11959))
- **$sanitize:** do not remove `tabindex` attribute
([799353c7](https://github.com/angular/angular.js/commit/799353c75de28e6fbf52dac6e0721e85b578575a),
[#8371](https://github.com/angular/angular.js/issues/8371), [#5853](https://github.com/angular/angular.js/issues/5853))
- **copy:** do not copy the same object twice
([0e622f7b](https://github.com/angular/angular.js/commit/0e622f7b5bc3d5d0ab0fbc1a1bc69404bd7216d5))
- **forms:** parse exponential notation in `numberInputType` directive
([ebd0fbba](https://github.com/angular/angular.js/commit/ebd0fbba8ff90bee0cd016d574643d56a7f81ed0),
[#12121](https://github.com/angular/angular.js/issues/12121), [#12122](https://github.com/angular/angular.js/issues/12122))
- **linky:** allow case insensitive scheme detection
([8dc09e6d](https://github.com/angular/angular.js/commit/8dc09e6dabb84c2c611cdc9e40adfac989648200),
[#12073](https://github.com/angular/angular.js/issues/12073), [#12073](https://github.com/angular/angular.js/issues/12073))
- **ngAria:**
- update `aria-valuemin/max` when `min/max` change
([ebaa0f59](https://github.com/angular/angular.js/commit/ebaa0f598501702ae64d59ada0ae492eaf0e2db6),
[#11770](https://github.com/angular/angular.js/issues/11770), [#11774](https://github.com/angular/angular.js/issues/11774))
- ensure boolean values for aria-hidden and aria-disabled
([59273354](https://github.com/angular/angular.js/commit/59273354b57dd8d1ad2cd2f4740ffa8923e480f9),
[#11365](https://github.com/angular/angular.js/issues/11365))
- **ngModel:** ignore Object.prototype properties on the form validation object
([0934b76b](https://github.com/angular/angular.js/commit/0934b76b72cec86093414834ac4cb7f0946b651d),
[#12066](https://github.com/angular/angular.js/issues/12066))
- **ngOptions:**
- do not watch properties starting with $
([34a6da24](https://github.com/angular/angular.js/commit/34a6da24c17356d4ffc70aec3f621a140a9a61ab),
[#11930](https://github.com/angular/angular.js/issues/11930), [#12010](https://github.com/angular/angular.js/issues/12010))
- use reference check only when not using trackBy
([d7dc14dc](https://github.com/angular/angular.js/commit/d7dc14dc0cdeb9c187d227e19acc8aca7df9d740),
[#11936](https://github.com/angular/angular.js/issues/11936), [#11996](https://github.com/angular/angular.js/issues/11996))
## Features
- **$compile:** show module name during `multidir` error
([351fe4b7](https://github.com/angular/angular.js/commit/351fe4b79c50a45a11af2fcd2aa7b6fd3b70058d),
[#11775](https://github.com/angular/angular.js/issues/11775))
- **$q:** $q.resolve as an alias for $q.when
([3ef52980](https://github.com/angular/angular.js/commit/3ef529806fef28b41ca4af86a330f39a95699cf6),
[#11944](https://github.com/angular/angular.js/issues/11944), [#11987](https://github.com/angular/angular.js/issues/11987))
## Performance Improvements
- **$compile:** avoid jquery data calls when there is no data
([9efb0d5e](https://github.com/angular/angular.js/commit/9efb0d5ee961b57c8fc144a3138a15955e4010e2))
<a name="1.3.16"></a>
# 1.3.16 cookie-oatmealification (2015-06-05)
## Bug Fixes
- **$compile:** throw error on invalid directive name
([634e4671](https://github.com/angular/angular.js/commit/634e467172efa696eb32ef8942ffbedeecbd030e),
[#11281](https://github.com/angular/angular.js/issues/11281), [#11109](https://github.com/angular/angular.js/issues/11109))
- **$cookies:** update $cookies to prevent duplicate cookie writes and play nice with external code
([706a93ab](https://github.com/angular/angular.js/commit/706a93ab6960e3474698ccf9a8048b3c32e567c6),
[#11490](https://github.com/angular/angular.js/issues/11490), [#11515](https://github.com/angular/angular.js/issues/11515))
- **$http:** throw error if `success` and `error` methods do not receive a function
([731e1f65](https://github.com/angular/angular.js/commit/731e1f6534ab7fd1e053b8d7a25c902fcd934fea),
[#11330](https://github.com/angular/angular.js/issues/11330), [#11333](https://github.com/angular/angular.js/issues/11333))
- **core:** ensure that multiple requests to requestAnimationFrame are buffered
([0adc0364](https://github.com/angular/angular.js/commit/0adc0364265b06c567ccc8e90a7f09cc46f235b2),
[#11791](https://github.com/angular/angular.js/issues/11791))
- **filterFilter:** fix matching against `null`/`undefined`
([9dd0fe35](https://github.com/angular/angular.js/commit/9dd0fe35d1027e59b84b2396abee00d8683f3b50),
[#11573](https://github.com/angular/angular.js/issues/11573), [#11617](https://github.com/angular/angular.js/issues/11617))
- **jqLite:**
- check for "length" in obj in isArrayLike to prevent iOS8 JIT bug from surfacing
([647f3f55](https://github.com/angular/angular.js/commit/647f3f55eb7100a255272f7277f0f962de234a32),
[#11508](https://github.com/angular/angular.js/issues/11508))
- attr should ignore comment, text and attribute nodes
([181e5ebc](https://github.com/angular/angular.js/commit/181e5ebc3fce5312feacaeace4fcad0d32f4d73c))
- **ngAnimate:**
- ensure that minified repaint code isn't removed
([d5c99ea4](https://github.com/angular/angular.js/commit/d5c99ea42b834343fd0362cfc572f47e7536ccfb),
[#9936](https://github.com/angular/angular.js/issues/9936))
- **ngAria:** handle elements with role="checkbox/menuitemcheckbox"
([1c282af5](https://github.com/angular/angular.js/commit/1c282af5abc205d4aac37c05c5cb725d71747134),
[#11317](https://github.com/angular/angular.js/issues/11317), [#11321](https://github.com/angular/angular.js/issues/11321))
- **ngModel:** allow setting model to NaN when asyncValidator is present
([b64519fe](https://github.com/angular/angular.js/commit/b64519fea7f1a5ec75e32c4b71b012b827314153),
[#11315](https://github.com/angular/angular.js/issues/11315), [#11411](https://github.com/angular/angular.js/issues/11411))
- **ngTouch:**
- check undefined tagName for SVG event target
([7560a8d2](https://github.com/angular/angular.js/commit/7560a8d2d65955ddb60ede9d586502f4e3cbd062))
- register touches properly when jQuery is used
([40441f6d](https://github.com/angular/angular.js/commit/40441f6dfc5ebd5cdc679c269c4639238f5351eb),
[#4001](https://github.com/angular/angular.js/issues/4001), [#8584](https://github.com/angular/angular.js/issues/8584), [#10797](https://github.com/angular/angular.js/issues/10797), [#11488](https://github.com/angular/angular.js/issues/11488))
- **select:** prevent unknown option being added to select when bound to null property
([9e3f82bb](https://github.com/angular/angular.js/commit/9e3f82bbaf83cad7bb3121db756099b0880562e6),
[#11872](https://github.com/angular/angular.js/issues/11872), [#11875](https://github.com/angular/angular.js/issues/11875))
## Features
- **travis:** run unit tests on iOS 8
([1f650871](https://github.com/angular/angular.js/commit/1f650871266b88b3dab4a894a839a82ac9a06b69),
[#11479](https://github.com/angular/angular.js/issues/11479))
<a name="1.4.0"></a>
# 1.4.0 jaracimrman-existence (2015-05-26)
@@ -374,7 +501,7 @@ To get the desired behaviour you need to iterate using the object form of the `n
## Breaking Changes
- **$animate:** due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
JavaSript and CSS animations can no longer be run in
JavaScript and CSS animations can no longer be run in
parallel. With earlier versions of ngAnimate, both CSS and JS animations
would be run together when multiple animations were detected. This
feature has now been removed, however, the same effect, with even more
@@ -420,7 +547,7 @@ $animate.off(element, 'enter', fn);
- **$animate:** due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
There is no need to call `$scope.$apply` or
`$scope.$digest` inside of a animation promise callback anymore
`$scope.$digest` inside of an animation promise callback anymore
since the promise is resolved within a digest automatically (but a
digest is not run unless the promise is chained).
@@ -1153,7 +1280,33 @@ But in practice this is not what people want and so this change iterates over pr
in the order they are returned by Object.keys(obj), which is almost always the order
in which the properties were defined.
- **select:** due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
the `select` directive will now use strict comparison of the `ngModel` scope value against `option`
values to determine which option is selected. This means `Number` scope values will not be matched
against numeric option strings.
In Angular 1.3.x, setting `scope.x = 200` would select the `option` with the value 200 in the following `select`:
```
<select ng-model="x">
<option value="100">100</option>
<option value="200">200</option>
</select>
```
In Angular 1.4.x, the 'unknown option' will be selected.
To remedy this, you can simply initialize the model as a string: `scope.x = '200'`, or if you want to
keep the model as a `Number`, you can do the conversion via `$formatters` and `$parsers` on `ngModel`:
```js
ngModelCtrl.$parsers.push(function(value) {
return parseInt(value, 10); // Convert option value to number
});
ngModelCtrl.$formatters.push(function(value) {
return value.toString(); // Convert scope value to string
});
```
<a name="1.3.9"></a>
# 1.3.9 multidimensional-awareness (2015-01-13)
+1 -1
View File
@@ -158,7 +158,7 @@ module.exports = function(grunt) {
jscs: {
src: ['src/**/*.js', 'test/**/*.js'],
options: {
config: ".jscs.json"
config: ".jscsrc"
}
},
+2 -2
View File
@@ -1,8 +1,8 @@
Using AngularJS with the Closure Compiler
=========================================
The Closure Compiler project contains externs definitions for AngularJS
JavaScript in its `contrib/externs` directory.
The Closure Compiler project contains definitions for the 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
+1 -1
View File
@@ -10,7 +10,7 @@ the browser how to do dependency injection and inversion of control.
Oh yeah and it helps with server-side communication, taming async callbacks with promises and
deferreds. It also makes client-side navigation and deeplinking with hashbang urls or HTML5 pushState a
piece of cake. The best of all: it makes development fun!
piece of cake. Best of all?? It makes development fun!
* Web site: http://angularjs.org
* Tutorial: http://docs.angularjs.org/tutorial
+2 -2
View File
@@ -35,7 +35,7 @@ angular.module('tutorials', [])
'step': '@docTutorialReset'
},
template:
'<p><a href="" ng-click="show=!show;$event.stopPropagation()">Workspace Reset Instructions ➤</a></p>\n' +
'<p><button class="btn" ng-click="show=!show">Workspace Reset Instructions ➤</button></p>\n' +
'<div class="alert alert-info" ng-show="show">\n' +
' <p>Reset the workspace to step {{step}}.</p>' +
' <p><pre>git checkout -f step-{{step}}</pre></p>\n' +
@@ -43,7 +43,7 @@ angular.module('tutorials', [])
'<a href="http://angular.github.io/angular-phonecat/step-{{step}}/app">Step {{step}} Live Demo</a>.</p>\n' +
'</div>\n' +
'<p>The most important changes are listed below. You can see the full diff on ' +
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{step ? (step - 1): \'0~1\'}}...step-{{step}}">GitHub</a>\n' +
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{step ? (step - 1): \'0~1\'}}...step-{{step}}" title="See diff on Github">GitHub</a>\n' +
'</p>'
};
});
+2 -3
View File
@@ -10,14 +10,14 @@ var Package = require('dgeni').Package;
module.exports = new Package('angularjs', [
require('dgeni-packages/ngdoc'),
require('dgeni-packages/nunjucks'),
require('dgeni-packages/examples')
require('dgeni-packages/examples'),
require('dgeni-packages/git')
])
.factory(require('./services/errorNamespaceMap'))
.factory(require('./services/getMinerrInfo'))
.factory(require('./services/getVersion'))
.factory(require('./services/gitData'))
.factory(require('./services/deployments/debug'))
.factory(require('./services/deployments/default'))
@@ -26,7 +26,6 @@ module.exports = new Package('angularjs', [
.factory(require('./inline-tag-defs/type'))
.processor(require('./processors/error-docs'))
.processor(require('./processors/index-page'))
.processor(require('./processors/keywords'))
-16
View File
@@ -1,16 +0,0 @@
"use strict";
var versionInfo = require('../../../lib/versions/version-info');
/**
* @dgService gitData
* @description
* Information from the local git repository
*/
module.exports = function gitData() {
return {
version: versionInfo.currentVersion,
versions: versionInfo.previousVersions,
info: versionInfo.gitRepoInfo
};
};
@@ -76,7 +76,7 @@
<div class="row">
<div class="col-md-9 header-branding">
<a class="brand navbar-brand" href="http://angularjs.org">
<img width="117" height="30" class="logo" ng-src="img/angularjs-for-header-only.svg">
<img width="117" height="30" class="logo" alt="Link to Angular JS Homepage" ng-src="img/angularjs-for-header-only.svg">
</a>
<ul class="nav navbar-nav">
<li class="divider-vertical"></li>
@@ -223,7 +223,7 @@
Super-powered by Google ©2010-2015
( <a id="version"
ng-href="https://github.com/angular/angular.js/blob/master/CHANGELOG.md#{{versionNumber}}"
ng-bind-template="v{{version}}">
ng-bind-template="v{{version}}" title="Changelog of this version of Angular JS">
</a>
)
</p>
+1 -1
View File
@@ -693,7 +693,7 @@ A path should always begin with forward slash (`/`); the `$location.path()` sett
forward slash if it is missing.
Note that the `!` prefix in the hashbang mode is not part of `$location.path()`; it is actually
hashPrefix.
`hashPrefix`.
## Crawling your app
+2 -2
View File
@@ -57,7 +57,7 @@ Try out the Live Preview above, and then let's walk through the example and desc
This looks like normal HTML, with some new markup. In Angular, a file like this is called a
<a name="template">{@link templates template}</a>. When Angular starts your application, it parses and
processes this new markup from the template using the <a name="compiler">{@link compiler compiler}</a>.
The loaded, transformed and rendered DOM is then called the <a name="view">view</a>.
The loaded, transformed and rendered DOM is then called the <a name="view"></a>*view*.
The first kind of new markup are the <a name="directive">{@link directive directives}</a>.
They apply special behavior to attributes or elements in the HTML. In the example above we use the
@@ -79,7 +79,7 @@ An <a name="expression">{@link expression expression}</a> in a template is a Jav
to read and write variables. Note that those variables are not global variables.
Just like variables in a JavaScript function live in a scope,
Angular provides a <a name="scope">{@link scope scope}</a> for the variables accessible to expressions.
The values that are stored in variables on the scope are referred to as the <a name="model">model</a>
The values that are stored in variables on the scope are referred to as the <a name="model"></a>*model*
in the rest of the documentation.
Applied to the example above, the markup directs Angular to "take the data we got from the input widgets
and multiply them together".
+1 -1
View File
@@ -51,7 +51,7 @@ In the following example, we say that the `<input>` element **matches** the `ngM
The following also **matches** `ngModel`:
```html
<input data-ng:model="foo">
<input data-ng-model="foo">
```
### Normalization
+14 -11
View File
@@ -28,13 +28,13 @@ Angular expressions are like JavaScript expressions with the following differenc
* **No Control Flow Statements:** You cannot use the following in an Angular expression:
conditionals, loops, or exceptions.
* **No Function Declarations:** You cannot declare functions in an Angular expression,
even inside `ng-init` directive.
* **No RegExp Creation With Literal Notation:** You cannot create regular expressions
* **No RegExp Creation With Literal Notation:** You cannot create regular expressions
in an Angular expression.
* **No Comma And Void Operators:** You cannot use `,` or `void` in an Angular expression.
* **Filters:** You can use {@link guide/filter filters} within expressions to format data before
@@ -175,7 +175,7 @@ expression, delegate to a JavaScript method instead.
## No function declarations or RegExp creation with literal notation
You can't declare functions or create regular expressions from within AngularJS expressions. This is
to avoid complex model transformation logic inside templates. Such logic is better placed in a
to avoid complex model transformation logic inside templates. Such logic is better placed in a
controller or in a dedicated filter where it can be tested properly.
## `$event`
@@ -303,19 +303,23 @@ then the expression is not fulfilled and will remain watched.
keep dirty-checking the watch in the future digest loops by following the same
algorithm starting from step 1
#### Special case for object literals
Unlike simple values, object-literals are watched until every key is defined.
See http://www.bennadel.com/blog/2760-one-time-data-bindings-for-object-literal-expressions-in-angularjs-1-3.htm
### How to benefit from one-time binding
If the expression will not change once set, it is a candidate for one-time binding.
If the expression will not change once set, it is a candidate for one-time binding.
Here are three example cases.
When interpolating text or attributes:
```html
<div name="attr: {{::color}}">text: {{::name}}</div>
<div name="attr: {{::color}}">text: {{::name | uppercase}}</div>
```
When using a directive with bidirectional binding and the parameters will not change:
When using a directive with bidirectional binding and parameters that will not change:
```js
someModule.directive('someDirective', function() {
@@ -338,7 +342,6 @@ When using a directive that takes an expression:
```html
<ul>
<li ng-repeat="item in ::items">{{item.name}};</li>
<li ng-repeat="item in ::items | orderBy:'name'">{{item.name}};</li>
</ul>
```
```
+1 -3
View File
@@ -491,9 +491,7 @@ The following example shows how to add two-way data-binding to contentEditable e
link: function(scope, elm, attrs, ctrl) {
// view -> model
elm.on('blur', function() {
scope.$apply(function() {
ctrl.$setViewValue(elm.html());
});
ctrl.$setViewValue(elm.html());
});
// model -> view
+1 -1
View File
@@ -305,7 +305,7 @@ matching is **case-sensitive**.
#### Selection Keywords
Selection keywords are simple words like "male" and "female". The keyword, "other", and it's
Selection keywords are simple words like "male" and "female". The keyword, "other", and its
corresponding message are required while others are optional. It is used when the Angular
expression does not match (case-insensitively) any of the other keywords specified.
+38 -6
View File
@@ -43,7 +43,7 @@ Animations in 1.4 have been refactored internally, but the API has stayed much t
some breaking changes that need to be addressed when upgrading to 1.4.
Due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
JavaSript and CSS animations can no longer be run in
JavaScript and CSS animations can no longer be run in
parallel. With earlier versions of ngAnimate, both CSS and JS animations
would be run together when multiple animations were detected. This
feature has been removed, however, the same effect, with even more
@@ -88,10 +88,10 @@ element.on('$animate:before', function(e, data) {
element.off('$animate:before', fn);
// 1.4+
$animate.on(element, 'enter', function(data) {
$animate.on('enter', element, function(data) {
//...
});
$animate.off(element, 'enter', fn);
$animate.off('enter', element, fn);
```
Due to [c8700f04](https://github.com/angular/angular.js/commit/c8700f04fb6fb5dc21ac24de8665c0476d6db5ef),
@@ -136,7 +136,7 @@ class based animations (animations triggered via ngClass) in order to ensure tha
## Forms (`ngMessages`, `ngOptions`)
## Forms (`ngMessages`, `ngOptions`, `select`)
### ngMessages
The ngMessages module has also been subject to an internal refactor to allow it to be more flexible
@@ -178,8 +178,8 @@ have been fixed. The breaking changes are comparatively minor and should not aff
Due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
when `ngOptions` renders the option values within the DOM, the resulting HTML code is different.
Normally this should not affect your application at all, however, if your code relies on inspecting
the value property of `<option>` elements (that `ngOptions` generates) then be sure to [read the details]
(https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef).
the value property of `<option>` elements (that `ngOptions` generates) then be sure
to [read the details](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef).
Due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
when iterating over an object's properties using the `(key, value) in obj` syntax
@@ -190,6 +190,35 @@ in the order they are returned by Object.keys(obj), which is almost always the o
in which the properties were defined.
### select
Due to [7fda214c](https://github.com/angular/angular.js/commit/7fda214c4f65a6a06b25cf5d5aff013a364e9cef),
the `select` directive will now use strict comparison of the `ngModel` scope value against `option`
values to determine which option is selected. This means `Number` scope values will not be matched
against numeric option strings.
In Angular 1.3.x, setting `scope.x = 200` would select the option with the value 200 in the following `select`:
```
<select ng-model="x">
<option value="100">100</option>
<option value="200">200</option>
</select>
```
In Angular 1.4.x, the 'unknown option' will be selected.
To remedy this, you can simply initialize the model as a string: `scope.x = '200'`, or if you want to
keep the model as a `Number`, you can do the conversion via `$formatters` and `$parsers` on `ngModel`:
```js
ngModelCtrl.$parsers.push(function(value) {
return parseInt(value, 10); // Convert option value to number
});
ngModelCtrl.$formatters.push(function(value) {
return value.toString(); // Convert scope value to string
});
```
## Templating (`ngRepeat`, `$compile`)
### ngRepeat
@@ -217,6 +246,9 @@ Due to [6a38dbfd](https://github.com/angular/angular.js/commit/6a38dbfd3c34c8f9e
previously, '&' expressions would always set up a function in the isolate scope. Now, if the binding
is marked as optional and the attribute is not specified, no function will be added to the isolate scope.
Due to [62d514b](https://github.com/angular/angular.js/commit/62d514b06937cc7dd86e973ea11165c88343b42d),
returning an object from a controller constructor function will now override the scope. Views that use the
controllerAs method will no longer get the this reference, but the returned object.
## Cookies (`ngCookies`)
+1 -1
View File
@@ -140,7 +140,7 @@ The above is a suggestion. Tailor it to your needs.
# Module Loading & Dependencies
A module is a collection of configuration and run blocks which get applied to the application
during the bootstrap process. In its simplest form the module consist of a collection of two kinds
during the bootstrap process. In its simplest form the module consists of a collection of two kinds
of blocks:
1. **Configuration blocks** - get executed during the provider registrations and configuration
+1 -1
View File
@@ -5,7 +5,7 @@
# What are Scopes?
{@link ng.$rootScope.Scope scope} is an object that refers to the application
{@link ng.$rootScope.Scope Scope} is an object that refers to the application
model. It is an execution context for {@link expression expressions}. Scopes are
arranged in hierarchical structure which mimic the DOM structure of the application. Scopes can
watch {@link guide/expression expressions} and propagate events.
+1 -1
View File
@@ -39,7 +39,7 @@ In general, we recommend against this because it can create unintended XSS vecto
However, it's ok to mix server-side templating in the bootstrap template (`index.html`) as long
as user input cannot be used on the server to output html that would then be processed by Angular
in a way that would cause allow for arbitrary code execution.
in a way that would allow for arbitrary code execution.
For instance, you can use server-side templating to dynamically generate CSS, URLs, etc, but not
for generating templates that are bootstrapped/compiled by Angular.
+3 -1
View File
@@ -261,8 +261,10 @@ myModule.filter('length', function() {
describe('length filter', function() {
var $filter;
beforeEach(inject(function(_$filter_){
$filter= _$filter_;
$filter = _$filter_;
}));
it('returns 0 when given null', function() {
+1 -1
View File
@@ -194,7 +194,7 @@ Conditionally showing and hiding things using jQuery is a common pattern in othe
`ng-show` (and `ng-hide`) conditionally show and hide elements based on boolean expressions.
Describe the conditions for showing and hiding an element in terms of `$scope` variables:
<div ng-show="!loggedIn">Click <a href="#/login">here</a> to log in</div>
<div ng-show="!loggedIn"><a href="#/login">Click here to log in</a></div>
Note also the counterpart `ng-hide` and similar `ng-disabled`.
Note especially the powerful `ng-switch` that should be used instead of several mutually exclusive `ng-show`s.
+4 -2
View File
@@ -129,7 +129,8 @@ Once you have Node.js installed on your machine you can download the tool depend
npm install
```
This command will download the following tools, into the `node_modules` directory:
This command reads angular-phonecat's `package.json` file and downloads the following tools
into the `node_modules` directory:
- [Bower][bower] - client-side code package manager
- [Http-Server][http-server] - simple local static web server
@@ -198,7 +199,7 @@ http://localhost:8000/app/index.html
<div class="alert alert-info">
To serve the web app on a different ip address or port, edit the "start" script within package.json.
You can `-a` to set the address and `-p` to set the port.
You can use `-a` to set the address and `-p` to set the port.
</div>
### Running Unit Tests
@@ -270,6 +271,7 @@ It is good to run the end to end tests whenever you make changes to the HTML vie
that the application as a whole is executing correctly. It is very common to run End to End tests
before pushing a new commit of changes to a remote repository.
Now that you have set up your local machine, let's get started with the tutorial: {@link step_00 Step 0 - Bootstrapping}
[git]: http://git-scm.com/
[node]: http://nodejs.org/
+13 -13
View File
@@ -31,7 +31,7 @@ npm install
To see the app running in a browser, open a *separate* terminal/command line tab or window, then
run `npm start` to start the web server. Now, open a browser window for the app and navigate to
<a href="http://localhost:8000/app/" target="_blank">`http://localhost:8000/app/`</a>
<a href="http://localhost:8000/app/" target="_blank" title="Open app on localhost">`http://localhost:8000/app/`</a>
Note that if you already ran the master branch app prior to checking out step-0, you may see the cached
master version of the app in your browser window at this point. Just hit refresh to re-load the page.
@@ -91,22 +91,22 @@ being the element on which the `ngApp` directive was defined.
Nothing here {{'yet' + '!'}}
This line demonstrates two core features of Angular's templating capabilities:
This line demonstrates two core features of Angular's templating capabilities:
* a binding, denoted by double-curlies `{{ }}`
* a simple expression `'yet' + '!'` used in this binding.
* a binding, denoted by double-curlies `{{ }}`
* a simple expression `'yet' + '!'` used in this binding.
The binding tells Angular that it should evaluate an expression and insert the result into the
DOM in place of the binding. Rather than a one-time insert, as we'll see in the next steps, a
binding will result in efficient continuous updates whenever the result of the expression
evaluation changes.
The binding tells Angular that it should evaluate an expression and insert the result into the
DOM in place of the binding. Rather than a one-time insert, as we'll see in the next steps, a
binding will result in efficient continuous updates whenever the result of the expression
evaluation changes.
{@link guide/expression Angular expression} is a JavaScript-like code snippet that is
evaluated by Angular in the context of the current model scope, rather than within the scope of
the global context (`window`).
{@link guide/expression Angular expression} is a JavaScript-like code snippet that is
evaluated by Angular in the context of the current model scope, rather than within the scope of
the global context (`window`).
As expected, once this template is processed by Angular, the html page contains the text:
"Nothing here yet!".
As expected, once this template is processed by Angular, the html page contains the text:
"Nothing here yet!".
## Bootstrapping AngularJS apps
+22 -16
View File
@@ -2385,10 +2385,10 @@
}
},
"dgeni-packages": {
"version": "0.10.13",
"version": "0.10.17",
"dependencies": {
"catharsis": {
"version": "0.8.6",
"version": "0.8.7",
"dependencies": {
"underscore-contrib": {
"version": "0.3.0",
@@ -2404,10 +2404,10 @@
"version": "2.3.0",
"dependencies": {
"camel-case": {
"version": "1.1.1"
"version": "1.1.2"
},
"constant-case": {
"version": "1.1.0"
"version": "1.1.1"
},
"dot-case": {
"version": "1.1.1"
@@ -2428,7 +2428,7 @@
"version": "1.1.1"
},
"pascal-case": {
"version": "1.1.0"
"version": "1.1.1"
},
"path-case": {
"version": "1.1.1"
@@ -2440,10 +2440,10 @@
"version": "1.1.1"
},
"swap-case": {
"version": "1.1.0"
"version": "1.1.1"
},
"title-case": {
"version": "1.1.0"
"version": "1.1.1"
},
"upper-case": {
"version": "1.1.2"
@@ -2468,7 +2468,7 @@
}
},
"htmlparser2": {
"version": "3.8.2",
"version": "3.8.3",
"dependencies": {
"domhandler": {
"version": "2.3.0"
@@ -2518,21 +2518,21 @@
"version": "0.3.0",
"dependencies": {
"lru-cache": {
"version": "2.5.0"
"version": "2.6.4"
},
"sigmund": {
"version": "1.0.0"
"version": "1.0.1"
}
}
},
"nunjucks": {
"version": "1.2.0",
"version": "1.3.4",
"dependencies": {
"optimist": {
"version": "0.6.1",
"dependencies": {
"wordwrap": {
"version": "0.0.2"
"version": "0.0.3"
},
"minimist": {
"version": "0.0.10"
@@ -2552,10 +2552,10 @@
"version": "0.2.14",
"dependencies": {
"lru-cache": {
"version": "2.5.0"
"version": "2.6.4"
},
"sigmund": {
"version": "1.0.0"
"version": "1.0.1"
}
}
},
@@ -2582,10 +2582,10 @@
"version": "0.1.6"
},
"fsevents": {
"version": "0.3.5",
"version": "0.3.6",
"dependencies": {
"nan": {
"version": "1.5.3"
"version": "1.8.4"
}
}
}
@@ -2621,6 +2621,12 @@
}
}
},
"semver": {
"version": "4.3.6"
},
"shelljs": {
"version": "0.5.1"
},
"winston": {
"version": "0.7.3",
"dependencies": {
+207 -196
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -1,5 +1,6 @@
{
"name": "angularjs",
"license": "MIT",
"branchVersion": "^1.4.0-beta.0",
"branchPattern": "1.4.*",
"repository": {
+1
View File
@@ -55,6 +55,7 @@
"isBlob": false,
"isBoolean": false,
"isPromiseLike": false,
"hasCustomToString": false,
"trim": false,
"escapeForRegexp": false,
"isElement": false,
+33 -28
View File
@@ -352,8 +352,12 @@ function baseExtend(dst, objs, deep) {
var src = obj[key];
if (deep && isObject(src)) {
if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
baseExtend(dst[key], [src], true);
if (isDate(src)) {
dst[key] = new Date(src.valueOf());
} else {
if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
baseExtend(dst[key], [src], true);
}
} else {
dst[key] = src;
}
@@ -464,6 +468,11 @@ identity.$inject = [];
function valueFn(value) {return function() {return value;};}
function hasCustomToString(obj) {
return isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
}
/**
* @ngdoc function
* @name angular.isUndefined
@@ -795,9 +804,18 @@ function copy(source, destination, stackSource, stackDest) {
if (!destination) {
destination = source;
if (source) {
if (isObject(source)) {
var index;
if (stackSource && (index = stackSource.indexOf(source)) !== -1) {
return stackDest[index];
}
// TypedArray, Date and RegExp have specific copy functionality and must be
// pushed onto the stack before returning.
// Array and other objects create the base object and recurse to copy child
// objects. The array/object will be pushed onto the stack when recursed.
if (isArray(source)) {
destination = copy(source, [], stackSource, stackDest);
return copy(source, [], stackSource, stackDest);
} else if (isTypedArray(source)) {
destination = new source.constructor(source);
} else if (isDate(source)) {
@@ -805,9 +823,14 @@ function copy(source, destination, stackSource, stackDest) {
} else if (isRegExp(source)) {
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
destination.lastIndex = source.lastIndex;
} else if (isObject(source)) {
} else {
var emptyObject = Object.create(getPrototypeOf(source));
destination = copy(source, emptyObject, stackSource, stackDest);
return copy(source, emptyObject, stackSource, stackDest);
}
if (stackDest) {
stackSource.push(source);
stackDest.push(destination);
}
}
} else {
@@ -818,9 +841,6 @@ function copy(source, destination, stackSource, stackDest) {
stackDest = stackDest || [];
if (isObject(source)) {
var index = stackSource.indexOf(source);
if (index !== -1) return stackDest[index];
stackSource.push(source);
stackDest.push(destination);
}
@@ -829,12 +849,7 @@ function copy(source, destination, stackSource, stackDest) {
if (isArray(source)) {
destination.length = 0;
for (var i = 0; i < source.length; i++) {
result = copy(source[i], null, stackSource, stackDest);
if (isObject(source[i])) {
stackSource.push(source[i]);
stackDest.push(result);
}
destination.push(result);
destination.push(copy(source[i], null, stackSource, stackDest));
}
} else {
var h = destination.$$hashKey;
@@ -848,20 +863,20 @@ function copy(source, destination, stackSource, stackDest) {
if (isBlankObject(source)) {
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
for (key in source) {
putValue(key, source[key], destination, stackSource, stackDest);
destination[key] = copy(source[key], null, stackSource, stackDest);
}
} else if (source && typeof source.hasOwnProperty === 'function') {
// Slow path, which must rely on hasOwnProperty
for (key in source) {
if (source.hasOwnProperty(key)) {
putValue(key, source[key], destination, stackSource, stackDest);
destination[key] = copy(source[key], null, stackSource, stackDest);
}
}
} else {
// Slowest path --- hasOwnProperty can't be called as a method
for (key in source) {
if (hasOwnProperty.call(source, key)) {
putValue(key, source[key], destination, stackSource, stackDest);
destination[key] = copy(source[key], null, stackSource, stackDest);
}
}
}
@@ -869,16 +884,6 @@ function copy(source, destination, stackSource, stackDest) {
}
}
return destination;
function putValue(key, val, destination, stackSource, stackDest) {
// No context allocation, trivial outer scope, easily inlined
var result = copy(val, null, stackSource, stackDest);
if (isObject(val)) {
stackSource.push(val);
stackDest.push(result);
}
destination[key] = result;
}
}
/**
+1 -1
View File
@@ -645,7 +645,7 @@ function createInjector(modulesToLoad, strictDi) {
}));
forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
return instanceInjector;
+9 -1
View File
@@ -182,6 +182,13 @@ function jqLiteAcceptsData(node) {
return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
}
function jqLiteHasData(node) {
for (var key in jqCache[node.ng339]) {
return true;
}
return false;
}
function jqLiteBuildFragment(html, context) {
var tmp, tag, wrap,
fragment = context.createDocumentFragment(),
@@ -556,7 +563,8 @@ function getAliasedAttrName(element, name) {
forEach({
data: jqLiteData,
removeData: jqLiteRemoveData
removeData: jqLiteRemoveData,
hasData: jqLiteHasData
}, function(fn, name) {
JQLite[name] = fn;
});
+21 -8
View File
@@ -146,7 +146,7 @@ function setupModuleLoader(window) {
* @description
* See {@link auto.$provide#provider $provide.provider()}.
*/
provider: invokeLater('$provide', 'provider'),
provider: invokeLaterAndSetModuleName('$provide', 'provider'),
/**
* @ngdoc method
@@ -157,7 +157,7 @@ function setupModuleLoader(window) {
* @description
* See {@link auto.$provide#factory $provide.factory()}.
*/
factory: invokeLater('$provide', 'factory'),
factory: invokeLaterAndSetModuleName('$provide', 'factory'),
/**
* @ngdoc method
@@ -168,7 +168,7 @@ function setupModuleLoader(window) {
* @description
* See {@link auto.$provide#service $provide.service()}.
*/
service: invokeLater('$provide', 'service'),
service: invokeLaterAndSetModuleName('$provide', 'service'),
/**
* @ngdoc method
@@ -203,7 +203,7 @@ function setupModuleLoader(window) {
* @description
* See {@link auto.$provide#decorator $provide.decorator()}.
*/
decorator: invokeLater('$provide', 'decorator'),
decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
/**
* @ngdoc method
@@ -237,7 +237,7 @@ function setupModuleLoader(window) {
* See {@link ng.$animateProvider#register $animateProvider.register()} and
* {@link ngAnimate ngAnimate module} for more information.
*/
animation: invokeLater('$animateProvider', 'register'),
animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
/**
* @ngdoc method
@@ -255,7 +255,7 @@ function setupModuleLoader(window) {
* (`myapp_subsection_filterx`).
* </div>
*/
filter: invokeLater('$filterProvider', 'register'),
filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
/**
* @ngdoc method
@@ -267,7 +267,7 @@ function setupModuleLoader(window) {
* @description
* See {@link ng.$controllerProvider#register $controllerProvider.register()}.
*/
controller: invokeLater('$controllerProvider', 'register'),
controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
/**
* @ngdoc method
@@ -280,7 +280,7 @@ function setupModuleLoader(window) {
* @description
* See {@link ng.$compileProvider#directive $compileProvider.directive()}.
*/
directive: invokeLater('$compileProvider', 'directive'),
directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
/**
* @ngdoc method
@@ -330,6 +330,19 @@ function setupModuleLoader(window) {
return moduleInstance;
};
}
/**
* @param {string} provider
* @param {string} method
* @returns {angular.Module}
*/
function invokeLaterAndSetModuleName(provider, method) {
return function(recipeName, factoryFunction) {
if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
invokeQueue.push([provider, method, arguments]);
return moduleInstance;
};
}
});
};
});
+2 -2
View File
@@ -63,7 +63,7 @@ function Browser(window, document, $log, $sniffer) {
function getHash(url) {
var index = url.indexOf('#');
return index === -1 ? '' : url.substr(index + 1);
return index === -1 ? '' : url.substr(index);
}
/**
@@ -147,7 +147,7 @@ function Browser(window, document, $log, $sniffer) {
// Do the assignment again so that those two variables are referentially identical.
lastHistoryState = cachedState;
} else {
if (!sameBase) {
if (!sameBase || reloadLocation) {
reloadLocation = url;
}
if (replace) {
+63 -33
View File
@@ -399,13 +399,16 @@
* * `controller` - the directive's required controller instance(s) - Instances are shared
* among all directives, which allows the directives to use the controllers as a communication
* channel. The exact value depends on the directive's `require` property:
* * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
* * `string`: the controller instance
* * `array`: array of controller instances
* * no controller(s) required: `undefined`
*
* If a required controller cannot be found, and it is optional, the instance is `null`,
* otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
*
* Note that you can also require the directive's own controller - it will be made available like
* like any other controller.
*
* * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
* This is the same as the `$transclude`
* parameter of directive controllers, see there for details.
@@ -852,6 +855,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (isObject(bindings.isolateScope)) {
directive.$$isolateBindings = bindings.isolateScope;
}
directive.$$moduleName = directiveFactory.$$moduleName;
directives.push(directive);
} catch (e) {
$exceptionHandler(e);
@@ -1423,8 +1427,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (nodeLinkFn.transcludeOnThisElement) {
childBoundTranscludeFn = createBoundTranscludeFn(
scope, nodeLinkFn.transclude, parentBoundTranscludeFn,
nodeLinkFn.elementTranscludeOnThisElement);
scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
} else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
childBoundTranscludeFn = parentBoundTranscludeFn;
@@ -1446,7 +1449,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
}
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) {
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
@@ -1545,6 +1548,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
break;
case NODE_TYPE_TEXT: /* Text Node */
if (msie === 11) {
// Workaround for #11781
while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
node.parentNode.removeChild(node.nextSibling);
}
}
addTextInterpolateDirective(directives, node.nodeValue);
break;
case NODE_TYPE_COMMENT: /* Comment */
@@ -1644,7 +1654,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
previousCompileContext = previousCompileContext || {};
var terminalPriority = -Number.MAX_VALUE,
newScopeDirective,
newScopeDirective = previousCompileContext.newScopeDirective,
controllerDirectives = previousCompileContext.controllerDirectives,
newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
templateDirective = previousCompileContext.templateDirective,
@@ -1810,6 +1820,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
controllerDirectives: controllerDirectives,
newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
newIsolateScopeDirective: newIsolateScopeDirective,
templateDirective: templateDirective,
nonTlbTranscludeDirective: nonTlbTranscludeDirective
@@ -1837,7 +1848,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
nodeLinkFn.elementTranscludeOnThisElement = hasElementTranscludeDirective;
nodeLinkFn.templateOnThisElement = hasTemplate;
nodeLinkFn.transclude = childTranscludeFn;
@@ -1998,9 +2008,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
for (i in elementControllers) {
controller = elementControllers[i];
var controllerResult = controller();
if (controllerResult !== controller.instance) {
// If the controller constructor has a return value, overwrite the instance
// from setupControllers and update the element data
controller.instance = controllerResult;
$element.data('$' + directive.name + 'Controller', controllerResult);
$element.data('$' + i + 'Controller', controllerResult);
if (controller === controllerForBindings) {
// Remove and re-install bindToController bindings
thisLinkFn.$$destroyBindings();
@@ -2192,7 +2205,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
$compileNode.empty();
$templateRequest($sce.getTrustedResourceUrl(templateUrl))
$templateRequest(templateUrl)
.then(function(content) {
var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
@@ -2300,11 +2313,18 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return a.index - b.index;
}
function assertNoDuplicate(what, previousDirective, directive, element) {
function wrapModuleNameIfDefined(moduleName) {
return moduleName ?
(' (module: ' + moduleName + ')') :
'';
}
if (previousDirective) {
throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}',
previousDirective.name, directive.name, what, startingTag(element));
throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
}
}
@@ -2485,26 +2505,28 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var fragment = document.createDocumentFragment();
fragment.appendChild(firstElementToRemove);
// Copy over user data (that includes Angular's $scope etc.). Don't copy private
// data here because there's no public interface in jQuery to do that and copying over
// event listeners (which is the main use of private data) wouldn't work anyway.
jqLite(newNode).data(jqLite(firstElementToRemove).data());
if (jqLite.hasData(firstElementToRemove)) {
// Copy over user data (that includes Angular's $scope etc.). Don't copy private
// data here because there's no public interface in jQuery to do that and copying over
// event listeners (which is the main use of private data) wouldn't work anyway.
jqLite(newNode).data(jqLite(firstElementToRemove).data());
// Remove data of the replaced element. We cannot just call .remove()
// on the element it since that would deallocate scope that is needed
// for the new node. Instead, remove the data "manually".
if (!jQuery) {
delete jqLite.cache[firstElementToRemove[jqLite.expando]];
} else {
// jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
// the replaced element. The cleanData version monkey-patched by Angular would cause
// the scope to be trashed and we do need the very same scope to work with the new
// element. However, we cannot just cache the non-patched version and use it here as
// that would break if another library patches the method after Angular does (one
// example is jQuery UI). Instead, set a flag indicating scope destroying should be
// skipped this one time.
skipDestroyOnNextJQueryCleanData = true;
jQuery.cleanData([firstElementToRemove]);
// Remove data of the replaced element. We cannot just call .remove()
// on the element it since that would deallocate scope that is needed
// for the new node. Instead, remove the data "manually".
if (!jQuery) {
delete jqLite.cache[firstElementToRemove[jqLite.expando]];
} else {
// jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
// the replaced element. The cleanData version monkey-patched by Angular would cause
// the scope to be trashed and we do need the very same scope to work with the new
// element. However, we cannot just cache the non-patched version and use it here as
// that would break if another library patches the method after Angular does (one
// example is jQuery UI). Instead, set a flag indicating scope destroying should be
// skipped this one time.
skipDestroyOnNextJQueryCleanData = true;
jQuery.cleanData([firstElementToRemove]);
}
}
for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
@@ -2545,9 +2567,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
lastValue,
parentGet, parentSet, compare;
if (!hasOwnProperty.call(attrs, attrName)) {
// In the case of user defined a binding with the same name as a method in Object.prototype but didn't set
// the corresponding attribute. We need to make sure subsequent code won't access to the prototype function
attrs[attrName] = undefined;
}
switch (mode) {
case '@':
if (!attrs[attrName] && !optional) {
destination[scopeName] = undefined;
}
attrs.$observe(attrName, function(value) {
destination[scopeName] = value;
});
@@ -2564,6 +2596,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return;
}
parentGet = $parse(attrs[attrName]);
if (parentGet.literal) {
compare = equals;
} else {
@@ -2602,9 +2635,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
break;
case '&':
// Don't assign Object.prototype method to scope
if (!attrs.hasOwnProperty(attrName) && optional) break;
parentGet = $parse(attrs[attrName]);
// Don't assign noop to destination if expression is not valid
+8 -1
View File
@@ -206,6 +206,13 @@
* @priority 100
*
* @description
* Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
*
* Note that this directive should not be used together with {@link ngModel `ngModel`},
* as this can lead to unexpected behavior.
*
* ### Why do we need `ngChecked`?
*
* The HTML specification does not require browsers to preserve the values of boolean attributes
* such as checked. (Their presence means true and their absence means false.)
* If we put an Angular interpolation expression into such an attribute then the
@@ -230,7 +237,7 @@
*
* @element INPUT
* @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
* then special attribute "checked" will be set on the element
* then the `checked` attribute will be set on the element
*/
+17 -4
View File
@@ -13,7 +13,7 @@
var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
@@ -612,6 +612,16 @@ var inputType = {
* error docs for more information and an example of how to convert your model if necessary.
* </div>
*
* ## Issues with HTML5 constraint validation
*
* In browsers that follow the
* [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
* `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
* If a non-number is entered in the input, the browser will report the value as an empty string,
* which means the view / model values in `ngModel` and subsequently the scope value
* will also be an empty string.
*
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
@@ -903,12 +913,15 @@ var inputType = {
* HTML radio button.
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string} value The value to which the expression should be set when selected.
* @param {string} value The value to which the `ngModel` expression should be set when selected.
* Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
* too. Use `ngValue` if you need complex models (`number`, `object`, ...).
* @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.
* @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
* is selected. Should be used instead of the `value` attribute if you need
* a non-string `ngModel` (`boolean`, `array`, ...).
*
* @example
<example name="radio-input-directive" module="radioExample">
+8 -4
View File
@@ -162,7 +162,7 @@ function classDirective(name, selector) {
* @example Example that demonstrates basic bindings via ngClass directive.
<example>
<file name="index.html">
<p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p>
<p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
<label>
<input type="checkbox" ng-model="deleted">
deleted (apply "strike" class)
@@ -173,7 +173,7 @@ function classDirective(name, selector) {
</label><br>
<label>
<input type="checkbox" ng-model="error">
error (apply "red" class)
error (apply "has-error" class)
</label>
<hr>
<p ng-class="style">Using String Syntax</p>
@@ -202,6 +202,10 @@ function classDirective(name, selector) {
.red {
color: red;
}
.has-error {
color: red;
background-color: yellow;
}
.orange {
color: orange;
}
@@ -212,13 +216,13 @@ function classDirective(name, selector) {
it('should let you toggle the class', function() {
expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
expect(ps.first().getAttribute('class')).not.toMatch(/red/);
expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
element(by.model('important')).click();
expect(ps.first().getAttribute('class')).toMatch(/bold/);
element(by.model('error')).click();
expect(ps.first().getAttribute('class')).toMatch(/red/);
expect(ps.first().getAttribute('class')).toMatch(/has-error/);
});
it('should let you toggle string example', function() {
+1
View File
@@ -4,6 +4,7 @@
* @ngdoc directive
* @name ngIf
* @restrict A
* @multiElement
*
* @description
* The `ngIf` directive removes or recreates a portion of the DOM tree based on an
+3 -3
View File
@@ -178,8 +178,8 @@
* @param {Object} angularEvent Synthetic event object.
* @param {String} src URL of content to load.
*/
var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate', '$sce',
function($templateRequest, $anchorScroll, $animate, $sce) {
var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
function($templateRequest, $anchorScroll, $animate) {
return {
restrict: 'ECA',
priority: 400,
@@ -215,7 +215,7 @@ var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate', '$sce
}
};
scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
scope.$watch(srcExp, function ngIncludeWatchAction(src) {
var afterAnimation = function() {
if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
$anchorScroll();
+4 -2
View File
@@ -1101,7 +1101,7 @@ var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
* - `debounce`: integer value which contains the debounce model update value in milliseconds. A
* value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
* custom value for each event. For example:
* `ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"`
* `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
* - `allowInvalid`: boolean value which indicates that the model can be set with values that did
* not validate correctly instead of the default behavior of setting the model to undefined.
* - `getterSetter`: boolean value which determines whether or not to treat functions bound to
@@ -1351,7 +1351,9 @@ function addSetValidityMethod(context) {
function isObjectEmpty(obj) {
if (obj) {
for (var prop in obj) {
return false;
if (obj.hasOwnProperty(prop)) {
return false;
}
}
}
return true;
+30 -23
View File
@@ -292,19 +292,41 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
this.disabled = disabled;
}
function getOptionValuesKeys(optionValues) {
var optionValuesKeys;
if (!keyName && isArrayLike(optionValues)) {
optionValuesKeys = optionValues;
} else {
// if object, extract keys, in enumeration order, unsorted
optionValuesKeys = [];
for (var itemKey in optionValues) {
if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
optionValuesKeys.push(itemKey);
}
}
}
return optionValuesKeys;
}
return {
trackBy: trackBy,
getTrackByValue: getTrackByValue,
getWatchables: $parse(valuesFn, function(values) {
getWatchables: $parse(valuesFn, function(optionValues) {
// Create a collection of things that we would like to watch (watchedArray)
// so that they can all be watched using a single $watchCollection
// that only runs the handler once if anything changes
var watchedArray = [];
values = values || [];
optionValues = optionValues || [];
Object.keys(values).forEach(function getWatchable(key) {
var locals = getLocals(values[key], key);
var selectValue = getTrackByValueFn(values[key], locals);
var optionValuesKeys = getOptionValuesKeys(optionValues);
var optionValuesLength = optionValuesKeys.length;
for (var index = 0; index < optionValuesLength; index++) {
var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
var value = optionValues[key];
var locals = getLocals(optionValues[key], key);
var selectValue = getTrackByValueFn(optionValues[key], locals);
watchedArray.push(selectValue);
// Only need to watch the displayFn if there is a specific label expression
@@ -318,7 +340,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var disableWhen = disableWhenFn(scope, locals);
watchedArray.push(disableWhen);
}
});
}
return watchedArray;
}),
@@ -330,21 +352,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
// The option values were already computed in the `getWatchables` fn,
// which must have been called to trigger `getOptions`
var optionValues = valuesFn(scope) || [];
var optionValuesKeys;
if (!keyName && isArrayLike(optionValues)) {
optionValuesKeys = optionValues;
} else {
// if object, extract keys, in enumeration order, unsorted
optionValuesKeys = [];
for (var itemKey in optionValues) {
if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
optionValuesKeys.push(itemKey);
}
}
}
var optionValuesKeys = getOptionValuesKeys(optionValues);
var optionValuesLength = optionValuesKeys.length;
for (var index = 0; index < optionValuesLength; index++) {
@@ -706,8 +714,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
// Check to see if the value has changed due to the update to the options
if (!ngModelCtrl.$isEmpty(previousValue)) {
var nextValue = selectCtrl.readValue();
if (ngOptions.trackBy && !equals(previousValue, nextValue) ||
previousValue !== nextValue) {
if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
ngModelCtrl.$setViewValue(nextValue);
ngModelCtrl.$render();
}
+13 -2
View File
@@ -3,6 +3,7 @@
/**
* @ngdoc directive
* @name ngRepeat
* @multiElement
*
* @description
* The `ngRepeat` directive instantiates a template once per item from a collection. Each template
@@ -97,6 +98,15 @@
* </div>
* ```
*
* <div class="alert alert-warning">
* **Note:** `track by` must always be the last expression:
* </div>
* ```
* <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
* {{model.name}}
* </div>
* ```
*
* # Special repeat start and end points
* To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
* the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
@@ -168,8 +178,9 @@
* which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
* is specified, ng-repeat associates elements by identity. It is an error to have
* more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
* mapped to the same DOM element, which is not possible.) If filters are used in the expression, they should be
* applied before the tracking expression.
* mapped to the same DOM element, which is not possible.)
*
* Note that the tracking expression must come last, after any filters, and the alias expression.
*
* For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
* will be associated by item identity in the array.
+2
View File
@@ -5,6 +5,7 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
/**
* @ngdoc directive
* @name ngShow
* @multiElement
*
* @description
* The `ngShow` directive shows or hides the given HTML element based on the expression
@@ -180,6 +181,7 @@ var ngShowDirective = ['$animate', function($animate) {
/**
* @ngdoc directive
* @name ngHide
* @multiElement
*
* @description
* The `ngHide` directive shows or hides the given HTML element based on the expression
+5 -7
View File
@@ -34,9 +34,11 @@
* `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
* **will** be matched by `{$: 'John'}`.
*
* - `function(value, index)`: A predicate function can be used to write arbitrary filters. The
* function is called for each element of `array`. The final result is an array of those
* elements that the predicate returned true for.
* - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
* The function is called for each element of the array, with the element, its index, and
* the entire array itself as arguments.
*
* The final result is an array of those elements that the predicate returned true for.
*
* @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
* determining if the expected value (from the filter expression) and actual value (from
@@ -161,10 +163,6 @@ function filterFilter() {
};
}
function hasCustomToString(obj) {
return isFunction(obj.toString) && obj.toString !== Object.prototype.toString;
}
// Helper functions for `filterFilter`
function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
+2 -1
View File
@@ -80,9 +80,10 @@ function currencyFilter($locale) {
* @description
* Formats a number as text.
*
* If the input is null or undefined, it will just be returned.
* If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
* If the input is not a number an empty string is returned.
*
* If the input is an infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
*
* @param {number|string} number Number to format.
* @param {(number|string)=} fractionSize Number of decimal places to round the number to.
+129 -82
View File
@@ -8,7 +8,7 @@
* @description
* Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
* for strings and numerically for numbers. Note: if you notice numbers are not being sorted
* correctly, make sure they are actually being saved as numbers and not strings.
* as expected, make sure they are actually being saved as numbers and not strings.
*
* @param {Array} array The array to sort.
* @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
@@ -83,19 +83,40 @@
{name:'Mike', phone:'555-4321', age:21},
{name:'Adam', phone:'555-5678', age:35},
{name:'Julie', phone:'555-8765', age:29}];
$scope.predicate = '-age';
$scope.predicate = 'age';
$scope.reverse = true;
$scope.order = function(predicate) {
$scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
$scope.predicate = predicate;
};
}]);
</script>
<style type="text/css">
.sortorder:after {
content: '\25b2';
}
.sortorder.reverse:after {
content: '\25bc';
}
</style>
<div ng-controller="ExampleController">
<pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
<hr/>
[ <a href="" ng-click="predicate=''">unsorted</a> ]
<table class="friend">
<tr>
<th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a>
(<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th>
<th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
<th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
<th>
<a href="" ng-click="order('name')">Name</a>
<span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('phone')">Phone Number</a>
<span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
</th>
<th>
<a href="" ng-click="order('age')">Age</a>
<span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
</th>
</tr>
<tr ng-repeat="friend in friends | orderBy:predicate:reverse">
<td>{{friend.name}}</td>
@@ -155,88 +176,114 @@
orderByFilter.$inject = ['$parse'];
function orderByFilter($parse) {
return function(array, sortPredicate, reverseOrder) {
if (!(isArrayLike(array))) return array;
sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate];
if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
if (sortPredicate.length === 0) { sortPredicate = ['+']; }
sortPredicate = sortPredicate.map(function(predicate) {
var descending = false, get = predicate || identity;
if (isString(predicate)) {
if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
descending = predicate.charAt(0) == '-';
predicate = predicate.substring(1);
}
if (predicate === '') {
// Effectively no predicate was passed so we compare identity
return reverseComparator(compare, descending);
}
get = $parse(predicate);
if (get.constant) {
var key = get();
return reverseComparator(function(a, b) {
return compare(a[key], b[key]);
}, descending);
}
}
return reverseComparator(function(a, b) {
return compare(get(a),get(b));
}, descending);
});
return slice.call(array).sort(reverseComparator(comparator, reverseOrder));
function comparator(o1, o2) {
for (var i = 0; i < sortPredicate.length; i++) {
var comp = sortPredicate[i](o1, o2);
if (comp !== 0) return comp;
}
return 0;
}
function reverseComparator(comp, descending) {
return descending
? function(a, b) {return comp(b,a);}
: comp;
var predicates = processPredicates(sortPredicate, reverseOrder);
// The next three lines are a version of a Swartzian Transform idiom from Perl
// (sometimes called the Decorate-Sort-Undecorate idiom)
// See https://en.wikipedia.org/wiki/Schwartzian_transform
var compareValues = Array.prototype.map.call(array, getComparisonObject);
compareValues.sort(doComparison);
array = compareValues.map(function(item) { return item.value; });
return array;
function getComparisonObject(value, index) {
return {
value: value,
predicateValues: predicates.map(function(predicate) {
return getPredicateValue(predicate.get(value), index);
})
};
}
function isPrimitive(value) {
switch (typeof value) {
case 'number': /* falls through */
case 'boolean': /* falls through */
case 'string':
return true;
default:
return false;
}
}
function objectToString(value) {
if (value === null) return 'null';
if (typeof value.valueOf === 'function') {
value = value.valueOf();
if (isPrimitive(value)) return value;
}
if (typeof value.toString === 'function') {
value = value.toString();
if (isPrimitive(value)) return value;
}
return '';
}
function compare(v1, v2) {
var t1 = typeof v1;
var t2 = typeof v2;
if (t1 === t2 && t1 === "object") {
v1 = objectToString(v1);
v2 = objectToString(v2);
}
if (t1 === t2) {
if (t1 === "string") {
v1 = v1.toLowerCase();
v2 = v2.toLowerCase();
}
if (v1 === v2) return 0;
return v1 < v2 ? -1 : 1;
} else {
return t1 < t2 ? -1 : 1;
function doComparison(v1, v2) {
var result = 0;
for (var index=0, length = predicates.length; index < length; ++index) {
result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
if (result) break;
}
return result;
}
};
function processPredicates(sortPredicate, reverseOrder) {
reverseOrder = reverseOrder ? -1 : 1;
return sortPredicate.map(function(predicate) {
var descending = 1, get = identity;
if (isFunction(predicate)) {
get = predicate;
} else if (isString(predicate)) {
if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
descending = predicate.charAt(0) == '-' ? -1 : 1;
predicate = predicate.substring(1);
}
if (predicate !== '') {
get = $parse(predicate);
if (get.constant) {
var key = get();
get = function(value) { return value[key]; };
}
}
}
return { get: get, descending: descending * reverseOrder };
});
}
function isPrimitive(value) {
switch (typeof value) {
case 'number': /* falls through */
case 'boolean': /* falls through */
case 'string':
return true;
default:
return false;
}
}
function objectValue(value, index) {
// If `valueOf` is a valid function use that
if (typeof value.valueOf === 'function') {
value = value.valueOf();
if (isPrimitive(value)) return value;
}
// If `toString` is a valid function and not the one from `Object.prototype` use that
if (hasCustomToString(value)) {
value = value.toString();
if (isPrimitive(value)) return value;
}
// We have a basic object so we use the position of the object in the collection
return index;
}
function getPredicateValue(value, index) {
var type = typeof value;
if (value === null) {
type = 'string';
value = 'null';
} else if (type === 'string') {
value = value.toLowerCase();
} else if (type === 'object') {
value = objectValue(value, index);
}
return { value: value, type: type };
}
function compare(v1, v2) {
var result = 0;
if (v1.type === v2.type) {
if (v1.value !== v2.value) {
result = v1.value < v2.value ? -1 : 1;
}
} else {
result = v1.type < v2.type ? -1 : 1;
}
return result;
}
}
+62 -15
View File
@@ -23,13 +23,17 @@ function $HttpParamSerializerProvider() {
* @name $httpParamSerializer
* @description
*
* Default $http params serializer that converts objects to a part of a request URL
* Default {@link $http `$http`} params serializer that converts objects to strings
* according to the following rules:
*
* * `{'foo': 'bar'}` results in `foo=bar`
* * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
* * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
* * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
*
* Note that serializer will sort the request parameters alphabetically.
* */
this.$get = function() {
return function ngParamSerializer(params) {
if (!params) return '';
@@ -56,7 +60,43 @@ function $HttpParamSerializerJQLikeProvider() {
* @name $httpParamSerializerJQLike
* @description
*
* Alternative $http params serializer that follows jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
* Alternative {@link $http `$http`} params serializer that follows
* jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
* The serializer will also sort the params alphabetically.
*
* To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
*
* ```js
* $http({
* url: myUrl,
* method: 'GET',
* params: myParams,
* paramSerializer: '$httpParamSerializerJQLike'
* });
* ```
*
* It is also possible to set it as the default `paramSerializer` in the
* {@link $httpProvider#defaults `$httpProvider`}.
*
* Additionally, you can inject the serializer and use it explicitly, for example to serialize
* form data for submission:
*
* ```js
* .controller(function($http, $httpParamSerializerJQLike) {
* //...
*
* $http({
* url: myUrl,
* method: 'POST',
* data: $httpParamSerializerJQLike(myData),
* headers: {
* 'Content-Type': 'application/x-www-form-urlencoded'
* }
* });
*
* });
* ```
*
* */
this.$get = function() {
return function jQueryLikeParamSerializer(params) {
@@ -213,7 +253,7 @@ function $HttpProvider() {
*
* - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
* that will provide the cache for all requests who set their `cache` property to `true`.
* If you set the `default.cache = false` then only requests that specify their own custom
* If you set the `defaults.cache = false` then only requests that specify their own custom
* cache object will be cached. See {@link $http#caching $http Caching} for more information.
*
* - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
@@ -230,10 +270,11 @@ function $HttpProvider() {
* - **`defaults.headers.put`**
* - **`defaults.headers.patch`**
*
* - **`defaults.paramSerializer`** - {string|function(Object<string,string>):string} - A function used to prepare string representation
* of request parameters (specified as an object).
* If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
* Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
*
* - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
* used to the prepare string representation of request parameters (specified as an object).
* If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
* Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
*
**/
var defaults = this.defaults = {
@@ -699,15 +740,17 @@ function $HttpProvider() {
* properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
* or the per-request config object.
*
* In order to prevent collisions in environments where multiple Angular apps share the
* same domain or subdomain, we recommend that each application uses unique cookie name.
*
*
* @param {object} config Object describing the request to be made and how it should be
* processed. The object has following properties:
*
* - **method** `{string}` HTTP method (e.g. 'GET', 'POST', etc)
* - **url** `{string}` Absolute or relative URL of the resource that is being requested.
* - **params** `{Object.<string|Object>}` Map of strings or objects which will be turned
* to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be
* JSONified.
* - **params** `{Object.<string|Object>}` Map of strings or objects which will be serialized
* with the `paramSerializer` and appended as GET parameters.
* - **data** `{string|Object}` Data to be sent as the request message data.
* - **headers** `{Object}` Map of strings or functions which return strings representing
* HTTP headers to send to the server. If the return value of a function is null, the
@@ -725,10 +768,14 @@ function $HttpProvider() {
* transform function or an array of such functions. The transform function takes the http
* response body, headers and status and returns its transformed (typically deserialized) version.
* See {@link ng.$http#overriding-the-default-transformations-per-request
* Overriding the Default Transformations}
* - **paramSerializer** - {string|function(Object<string,string>):string} - A function used to prepare string representation
* of request parameters (specified as an object).
* Is specified as string, it is interpreted as function registered in with the {$injector}.
* Overriding the Default TransformationjqLiks}
* - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
* prepare the string representation of request parameters (specified as an object).
* If specified as string, it is interpreted as function registered with the
* {@link $injector $injector}, which means you can create your own serializer
* by registering it as a {@link auto.$provide#service service}.
* The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
* alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
* - **cache** `{boolean|Cache}` If true, a default $http cache will be used to cache the
* GET request, otherwise if a cache instance built with
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
@@ -739,7 +786,7 @@ function $HttpProvider() {
* XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
* for more information.
* - **responseType** - `{string}` - see
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
* [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
*
* @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
* standard `then` method and two http specific methods: `success` and `error`. The `then`
+10 -2
View File
@@ -185,7 +185,7 @@ function LocationHashbangUrl(appBase, hashPrefix) {
var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
var withoutHashUrl;
if (withoutBaseUrl.charAt(0) === '#') {
if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
// The rest of the url starts with a hash so we have
// got either a hashbang path or a plain hash fragment
@@ -199,7 +199,15 @@ function LocationHashbangUrl(appBase, hashPrefix) {
// There was no hashbang path nor hash fragment:
// If we are in HTML5 mode we use what is left as the path;
// Otherwise we ignore what is left
withoutHashUrl = this.$$html5 ? withoutBaseUrl : '';
if (this.$$html5) {
withoutHashUrl = withoutBaseUrl;
} else {
withoutHashUrl = '';
if (isUndefined(withoutBaseUrl)) {
appBase = url;
this.replace();
}
}
}
parseAppUrl(withoutHashUrl, this);
+6 -2
View File
@@ -982,8 +982,10 @@ ASTCompiler.prototype = {
nameId.name = ast.property.name;
}
}
recursionFn(intoId);
}, function() {
self.assign(intoId, 'undefined');
});
recursionFn(intoId);
}, !!create);
break;
case AST.CallExpression:
@@ -1021,8 +1023,10 @@ ASTCompiler.prototype = {
}
expression = self.ensureSafeObject(expression);
self.assign(intoId, expression);
recursionFn(intoId);
}, function() {
self.assign(intoId, 'undefined');
});
recursionFn(intoId);
});
}
break;
+14
View File
@@ -497,6 +497,19 @@ function qFactory(nextTick, exceptionHandler) {
return result.promise.then(callback, errback, progressBack);
};
/**
* @ngdoc method
* @name $q#resolve
* @kind function
*
* @description
* Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
*
* @param {*} value Value or a promise
* @returns {Promise} Returns a promise of the passed value or promise
*/
var resolve = when;
/**
* @ngdoc method
* @name $q#all
@@ -565,6 +578,7 @@ function qFactory(nextTick, exceptionHandler) {
$Q.defer = defer;
$Q.reject = reject;
$Q.when = when;
$Q.resolve = resolve;
$Q.all = all;
return $Q;
+17 -6
View File
@@ -7,12 +7,14 @@ var $compileMinErr = minErr('$compile');
* @name $templateRequest
*
* @description
* The `$templateRequest` service downloads the provided template using `$http` and, upon success,
* stores the contents inside of `$templateCache`. If the HTTP request fails or the response data
* of the HTTP request is empty, a `$compile` error will be thrown (the exception can be thwarted
* by setting the 2nd parameter of the function to true).
* The `$templateRequest` service runs security checks then downloads the provided template using
* `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
* fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
* exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
* contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
* when `tpl` is of type string and `$templateCache` has the matching entry.
*
* @param {string} tpl The HTTP request template URL
* @param {string|TrustedResourceUrl} tpl The HTTP request template URL
* @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
*
* @return {Promise} a promise for the HTTP response data of the given URL.
@@ -20,10 +22,19 @@ var $compileMinErr = minErr('$compile');
* @property {number} totalPendingRequests total amount of pending template requests being downloaded.
*/
function $TemplateRequestProvider() {
this.$get = ['$templateCache', '$http', '$q', function($templateCache, $http, $q) {
this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
function handleRequestFn(tpl, ignoreRequestError) {
handleRequestFn.totalPendingRequests++;
// We consider the template cache holds only trusted templates, so
// there's no need to go through whitelisting again for keys that already
// are included in there. This also makes Angular accept any script
// directive, no matter its name. However, we still need to unwrap trusted
// types.
if (!isString(tpl) || !$templateCache.get(tpl)) {
tpl = $sce.getTrustedResourceUrl(tpl);
}
var transformResponse = $http.defaults && $http.defaults.transformResponse;
if (isArray(transformResponse)) {
+2 -1
View File
@@ -176,7 +176,8 @@
* unless you really need a digest to kick off afterwards.
*
* Keep in mind that, to make this easier, ngAnimate has tweaked the JS animations API to recognize when a runner instance is returned from $animateCss
* (so there is no need to call `runner.done(doneFn)` inside of your JavaScript animation code). Check the [animation code above](#usage) to see how this works.
* (so there is no need to call `runner.done(doneFn)` inside of your JavaScript animation code).
* Check the {@link ngAnimate.$animateCss#usage animation code above} to see how this works.
*
* @param {DOMElement} element the element that will be animated
* @param {object} options the animation-related options that will be applied during the animation
+6 -6
View File
@@ -72,7 +72,7 @@
* opacity:0;
* }
*
* /&#42; The starting CSS styles for the enter animation &#42;/
* /&#42; The finishing CSS styles for the enter animation &#42;/
* .fade.ng-enter.ng-enter-active {
* opacity:1;
* }
@@ -327,7 +327,7 @@
* ## CSS + JS Animations Together
*
* AngularJS 1.4 and higher has taken steps to make the amalgamation of CSS and JS animations more flexible. However, unlike earlier versions of Angular,
* defining CSS and JS animations to work off of the same CSS class will not work anymore. Therefore example below will only result in **JS animations taking
* defining CSS and JS animations to work off of the same CSS class will not work anymore. Therefore the example below will only result in **JS animations taking
* charge of the animation**:
*
* ```html
@@ -356,8 +356,8 @@
* }
* ```
*
* Does this mean that CSS and JS animations cannot be used together? Do JS-based animations always have higher priority? We can suppliment for the
* lack of CSS animations by making use of the `$animateCss` service to trigger our own tweaked-out, CSS-based animations directly from
* Does this mean that CSS and JS animations cannot be used together? Do JS-based animations always have higher priority? We can make up for the
* lack of CSS animations by using the `$animateCss` service to trigger our own tweaked-out, CSS-based animations directly from
* our own JS-based animation code:
*
* ```js
@@ -655,7 +655,7 @@
* ngModule.directive('greetingBox', ['$animate', function($animate) {
* return function(scope, element, attrs) {
* attrs.$observe('active', function(value) {
* value ? $animate.addClass(element, 'on') ? $animate.removeClass(element, 'on');
* value ? $animate.addClass(element, 'on') : $animate.removeClass(element, 'on');
* });
* });
* }]);
@@ -666,7 +666,7 @@
*
* ```css
* /&#42; normally we would create a CSS class to reference on the element &#42;/
* [greeting-box].on { transition:0.5s linear all; background:green; color:white; }
* greeting-box.on { transition:0.5s linear all; background:green; color:white; }
* ```
*
* The `$animate` service contains a variety of other methods like `enter`, `leave`, `animate` and `setClass`. To learn more about what's
+27 -12
View File
@@ -14,8 +14,8 @@
*
* ## Usage
*
* For ngAria to do its magic, simply include the module as a dependency. The directives supported
* by ngAria are:
* For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
* directives are supported:
* `ngModel`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`, `ngDblClick`, and `ngMessages`.
*
* Below is a more detailed breakdown of the attributes handled by ngAria:
@@ -83,7 +83,8 @@ function $AriaProvider() {
ariaMultiline: true,
ariaValue: true,
tabindex: true,
bindKeypress: true
bindKeypress: true,
bindRoleForClick: true
};
/**
@@ -102,6 +103,8 @@ function $AriaProvider() {
* - **tabindex** `{boolean}` Enables/disables tabindex tags
* - **bindKeypress** `{boolean}` Enables/disables keypress event binding on `&lt;div&gt;` and
* `&lt;li&gt;` elements with ng-click
* - **bindRoleForClick** `{boolean}` Adds role=button to non-interactive elements like `div`
* using ng-click, making them more accessible to users of assistive technologies
*
* @description
* Enables/disables various ARIA attributes
@@ -115,9 +118,8 @@ function $AriaProvider() {
var ariaCamelName = attr.$normalize(ariaAttr);
if (config[ariaCamelName] && !attr[ariaCamelName]) {
scope.$watch(attr[attrName], function(boolVal) {
if (negate) {
boolVal = !boolVal;
}
// ensure boolean value
boolVal = negate ? !boolVal : !!boolVal;
elem.attr(ariaAttr, boolVal);
});
}
@@ -265,13 +267,23 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
elem.attr('role', 'slider');
}
if ($aria.config('ariaValue')) {
if (attr.min && !elem.attr('aria-valuemin')) {
elem.attr('aria-valuemin', attr.min);
var needsAriaValuemin = !elem.attr('aria-valuemin') &&
(attr.hasOwnProperty('min') || attr.hasOwnProperty('ngMin'));
var needsAriaValuemax = !elem.attr('aria-valuemax') &&
(attr.hasOwnProperty('max') || attr.hasOwnProperty('ngMax'));
var needsAriaValuenow = !elem.attr('aria-valuenow');
if (needsAriaValuemin) {
attr.$observe('min', function ngAriaValueMinReaction(newVal) {
elem.attr('aria-valuemin', newVal);
});
}
if (attr.max && !elem.attr('aria-valuemax')) {
elem.attr('aria-valuemax', attr.max);
if (needsAriaValuemax) {
attr.$observe('max', function ngAriaValueMinReaction(newVal) {
elem.attr('aria-valuemax', newVal);
});
}
if (!elem.attr('aria-valuenow')) {
if (needsAriaValuenow) {
scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) {
elem.attr('aria-valuenow', newVal);
});
@@ -337,7 +349,10 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
return true;
}
}
if (!elem.attr('role') && !isNodeOneOf(elem, nodeBlackList)) {
if ($aria.config('bindRoleForClick')
&& !elem.attr('role')
&& !isNodeOneOf(elem, nodeBlackList)) {
elem.attr('role', 'button');
}
+1 -1
View File
@@ -15,7 +15,7 @@ angular.module('ngCookies').
* Requires the {@link ngCookies `ngCookies`} module to be installed.
*
* <div class="alert alert-danger">
* **Note:** The $cookieStore service is deprecated.
* **Note:** The $cookieStore service is **deprecated**.
* Please use the {@link ngCookies.$cookies `$cookies`} service instead.
* </div>
*
+7 -6
View File
@@ -12,8 +12,7 @@
*
* <div doc-module-components="ngCookies"></div>
*
* See {@link ngCookies.$cookies `$cookies`} and
* {@link ngCookies.$cookieStore `$cookieStore`} for usage.
* See {@link ngCookies.$cookies `$cookies`} for usage.
*/
@@ -43,7 +42,7 @@ angular.module('ngCookies', ['ng']).
* or a Date object indicating the exact date/time this cookie will expire.
* - **secure** - `{boolean}` - The cookie will be available only in secured connection.
*
* Note: by default the address that appears in your <base> tag will be used as path.
* Note: by default the address that appears in your `<base>` tag will be used as path.
* This is import so that cookies will be visible for all routes in case html5mode is enabled
*
**/
@@ -60,9 +59,11 @@ angular.module('ngCookies', ['ng']).
* @description
* Provides read/write access to browser's cookies.
*
* BREAKING CHANGE: `$cookies` no longer exposes properties that represent the
* current browser cookie values. Now you must use the get/put/remove/etc. methods
* as described below.
* <div class="alert alert-info">
* Up until Angular 1.3, `$cookies` exposed properties that represented the
* current browser cookie values. In version 1.4, this behavior has changed, and
* `$cookies` now provides a standard api of getters, setters etc.
* </div>
*
* Requires the {@link ngCookies `ngCookies`} module to be installed.
*
+2 -2
View File
@@ -51,7 +51,7 @@ var jqLite = angular.element;
*
* ```javascript
* <!-- keep in mind that ngModel automatically sets these error flags -->
* myField.$error = { minlength : true, required : false };
* myField.$error = { minlength : true, required : true };
* ```
*
* Then the `required` message will be displayed first. When required is false then the `minlength` message
@@ -251,7 +251,7 @@ angular.module('ngMessages', [])
*
* @description
* `ngMessages` is a directive that is designed to show and hide messages based on the state
* of a key/value object that it listens on. The directive itself compliments error message
* of a key/value object that it listens on. The directive itself complements error message
* reporting with the `ngModel` $error object (which stores a key/value state of validation errors).
*
* `ngMessages` manages the state of internal messages within its container element. The internal
+1 -1
View File
@@ -1083,7 +1083,7 @@ angular.mock.dump = function(object) {
$httpBackend.flush();
$httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
// check if the header was send, if it wasn't the expectation won't
// check if the header was sent, if it wasn't the expectation won't
// match the request and the test will fail
return headers['Authorization'] == 'xxx';
}).respond(201, '');
+2 -1
View File
@@ -209,7 +209,8 @@ function shallowClearAndCopy(src, dst) {
* - non-GET instance actions: `instance.$action([parameters], [success], [error])`
*
*
* Success callback is called with (value, responseHeaders) arguments. Error callback is called
* Success callback is called with (value, responseHeaders) arguments, where the value is
* the populated resource instance or collection object. The error callback is called
* with (httpResponse) argument.
*
* Class actions return empty instance (with additional properties below).
+1 -2
View File
@@ -591,9 +591,8 @@ function $RouteProvider() {
if (angular.isFunction(templateUrl)) {
templateUrl = templateUrl(nextRoute.params);
}
templateUrl = $sce.getTrustedResourceUrl(templateUrl);
if (angular.isDefined(templateUrl)) {
nextRoute.loadedTemplateUrl = templateUrl;
nextRoute.loadedTemplateUrl = $sce.valueOf(templateUrl);
template = $templateRequest(templateUrl);
}
}
+2 -2
View File
@@ -104,8 +104,8 @@
*/
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
var LINKY_URL_REGEXP =
/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"”’]/,
MAILTO_REGEXP = /^mailto:/;
/((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"”’]/i,
MAILTO_REGEXP = /^mailto:/i;
return function(text, target) {
if (!text) return text;
+1 -1
View File
@@ -228,7 +228,7 @@ var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap,xlink:href");
var htmlAttrs = makeMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
'scope,scrolling,shape,size,span,start,summary,target,title,type,' +
'scope,scrolling,shape,size,span,start,summary,tabindex,target,title,type,' +
'valign,value,vspace,width');
// SVG attributes (without "id" and "name" attributes)
+2 -1
View File
@@ -74,7 +74,8 @@ ngTouch.factory('$swipe', [function() {
* `$swipe` will listen for `mouse` and `touch` events.
*
* The four events are `start`, `move`, `end`, and `cancel`. `start`, `move`, and `end`
* receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }`.
* receive as a parameter a coordinates object of the form `{ x: 150, y: 310 }` and the raw
* `event`. `cancel` receives the raw `event` as its single parameter.
*
* `start` is called on either `mousedown` or `touchstart`. After this event, `$swipe` is
* watching for `touchmove` or `mousemove` events. These events are ignored until the total
+85 -2
View File
@@ -336,7 +336,7 @@ describe('angular', function() {
expect(hashKey(dst)).not.toEqual(hashKey(src));
});
it('should retain the previous $$hashKey', function() {
it('should retain the previous $$hashKey when copying object with hashKey', function() {
var src,dst,h;
src = {};
dst = {};
@@ -351,7 +351,21 @@ describe('angular', function() {
expect(hashKey(dst)).toEqual(h);
});
it('should handle circular references when circularRefs is turned on', function() {
it('should retain the previous $$hashKey when copying non-object', function() {
var dst = {};
var h = hashKey(dst);
copy(null, dst);
expect(hashKey(dst)).toEqual(h);
copy(42, dst);
expect(hashKey(dst)).toEqual(h);
copy(new Date(), dst);
expect(hashKey(dst)).toEqual(h);
});
it('should handle circular references', function() {
var a = {b: {a: null}, self: null, selfs: [null, null, [null]]};
a.b.a = a;
a.self = a;
@@ -362,13 +376,60 @@ describe('angular', function() {
expect(aCopy).not.toBe(a);
expect(aCopy).toBe(aCopy.self);
expect(aCopy).toBe(aCopy.selfs[2][0]);
expect(aCopy.selfs[2]).not.toBe(a.selfs[2]);
var copyTo = [];
aCopy = copy(a, copyTo);
expect(aCopy).toBe(copyTo);
expect(aCopy).not.toBe(a);
expect(aCopy).toBe(aCopy.self);
});
it('should handle objects with multiple references', function() {
var b = {};
var a = [b, -1, b];
var aCopy = copy(a);
expect(aCopy[0]).not.toBe(a[0]);
expect(aCopy[0]).toBe(aCopy[2]);
var copyTo = [];
aCopy = copy(a, copyTo);
expect(aCopy).toBe(copyTo);
expect(aCopy[0]).not.toBe(a[0]);
expect(aCopy[0]).toBe(aCopy[2]);
});
it('should handle date/regex objects with multiple references', function() {
var re = /foo/;
var d = new Date();
var o = {re: re, re2: re, d: d, d2: d};
var oCopy = copy(o);
expect(oCopy.re).toBe(oCopy.re2);
expect(oCopy.d).toBe(oCopy.d2);
oCopy = copy(o, {});
expect(oCopy.re).toBe(oCopy.re2);
expect(oCopy.d).toBe(oCopy.d2);
});
it('should clear destination arrays correctly when source is non-array', function() {
expect(copy(null, [1,2,3])).toEqual([]);
expect(copy(undefined, [1,2,3])).toEqual([]);
expect(copy({0: 1, 1: 2}, [1,2,3])).toEqual([1,2]);
expect(copy(new Date(), [1,2,3])).toEqual([]);
expect(copy(/a/, [1,2,3])).toEqual([]);
expect(copy(true, [1,2,3])).toEqual([]);
});
it('should clear destination objects correctly when source is non-array', function() {
expect(copy(null, {0:1,1:2,2:3})).toEqual({});
expect(copy(undefined, {0:1,1:2,2:3})).toEqual({});
expect(copy(new Date(), {0:1,1:2,2:3})).toEqual({});
expect(copy(/a/, {0:1,1:2,2:3})).toEqual({});
expect(copy(true, {0:1,1:2,2:3})).toEqual({});
});
it('should copy objects with no prototype parent', function() {
@@ -420,6 +481,16 @@ describe('angular', function() {
// make sure we retain the old key
expect(hashKey(dst)).toEqual(h);
});
it('should copy dates by reference', function() {
var src = { date: new Date() };
var dst = {};
extend(dst, src);
expect(dst.date).toBe(src.date);
});
});
@@ -487,6 +558,18 @@ describe('angular', function() {
});
expect(dst.foo).not.toBe(src.foo);
});
it('should copy dates by value', function() {
var src = { date: new Date() };
var dst = {};
merge(dst, src);
expect(dst.date).not.toBe(src.date);
expect(isDate(dst.date)).toBeTruthy();
expect(dst.date.valueOf()).toEqual(src.date.valueOf());
});
});
+6
View File
@@ -405,10 +405,13 @@ describe('jqLite', function() {
it('should provide the non-wrapped data calls', function() {
var node = document.createElement('div');
expect(jqLite.hasData(node)).toBe(false);
expect(jqLite.data(node, "foo")).toBeUndefined();
expect(jqLite.hasData(node)).toBe(false);
jqLite.data(node, "foo", "bar");
expect(jqLite.hasData(node)).toBe(true);
expect(jqLite.data(node, "foo")).toBe("bar");
expect(jqLite(node).data("foo")).toBe("bar");
@@ -421,6 +424,9 @@ describe('jqLite', function() {
jqLite.removeData(node);
jqLite.removeData(node);
expect(jqLite.data(node, "bar")).toBeUndefined();
jqLite(node).remove();
expect(jqLite.hasData(node)).toBe(false);
});
it('should emit $destroy event if element removed via remove()', function() {
+3 -1
View File
@@ -57,7 +57,9 @@ function MockWindow(options) {
return getHash(locationHref);
},
set hash(value) {
locationHref = stripHash(locationHref) + '#' + value;
// replace the hash with the new one (stripping off a leading hash if there is one)
// See hash setter spec: https://url.spec.whatwg.org/#urlutils-and-urlutilsreadonly-members
locationHref = stripHash(locationHref) + '#' + value.replace(/^#/,'');
},
replace: function(url) {
locationHref = url;
+300 -6
View File
@@ -1296,14 +1296,24 @@ describe('$compile', function() {
));
it('should not load cross domain templates by default', inject(
function($compile, $rootScope, $templateCache, $sce) {
function($compile, $rootScope) {
expect(function() {
$templateCache.put('http://example.com/should-not-load.html', 'Should not load even if in cache.');
$compile('<div class="crossDomainTemplate"></div>')($rootScope);
}).toThrowMinErr('$sce', 'insecurl', 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: http://example.com/should-not-load.html');
}
));
it('should trust what is already in the template cache', inject(
function($compile, $httpBackend, $rootScope, $templateCache) {
$httpBackend.expect('GET', 'http://example.com/should-not-load.html').respond('<span>example.com/remote-version</span>');
$templateCache.put('http://example.com/should-not-load.html', '<span>example.com/cached-version</span>');
element = $compile('<div class="crossDomainTemplate"></div>')($rootScope);
expect(sortedHtml(element)).toEqual('<div class="crossDomainTemplate"></div>');
$rootScope.$digest();
expect(sortedHtml(element)).toEqual('<div class="crossDomainTemplate"><span>example.com/cached-version</span></div>');
}
));
it('should load cross domain templates when trusted', inject(
function($compile, $httpBackend, $rootScope, $sce) {
$httpBackend.expect('GET', 'http://example.com/trusted-template.html').respond('<span>example.com/trusted_template_contents</span>');
@@ -2231,6 +2241,45 @@ describe('$compile', function() {
}}
};
});
directive('prototypeMethodNameAsScopeVarA', function() {
return {
scope: {
'constructor': '=?',
'valueOf': '='
},
restrict: 'AE',
template: '<span></span>'
};
});
directive('prototypeMethodNameAsScopeVarB', function() {
return {
scope: {
'constructor': '@?',
'valueOf': '@'
},
restrict: 'AE',
template: '<span></span>'
};
});
directive('prototypeMethodNameAsScopeVarC', function() {
return {
scope: {
'constructor': '&?',
'valueOf': '&'
},
restrict: 'AE',
template: '<span></span>'
};
});
directive('watchAsScopeVar', function() {
return {
scope: {
'watch': '='
},
restrict: 'AE',
template: '<span></span>'
};
});
}));
@@ -2331,7 +2380,7 @@ describe('$compile', function() {
})
);
it('should not allow more then one isolate scope creation per element', inject(
it('should not allow more than one isolate scope creation per element', inject(
function($rootScope, $compile) {
expect(function() {
$compile('<div class="iscope-a; scope-b"></div>');
@@ -2340,6 +2389,18 @@ describe('$compile', function() {
})
);
it('should not allow more than one isolate/new scope creation per element regardless of `templateUrl`',
inject(function($httpBackend) {
$httpBackend.expect('GET', 'tiscope.html').respond('<div>Hello, world !</div>');
expect(function() {
compile('<div class="tiscope-a; scope-b"></div>');
$httpBackend.flush();
}).toThrowMinErr('$compile', 'multidir', 'Multiple directives [scopeB, tiscopeA] ' +
'asking for new/isolated scope on: <div class="tiscope-a; scope-b ng-scope">');
})
);
it('should not allow more than one isolate scope creation per element regardless of directive priority', function() {
module(function($compileProvider) {
$compileProvider.directive('highPriorityScope', function() {
@@ -2472,6 +2533,118 @@ describe('$compile', function() {
expect(element.isolateScope()).not.toBe($rootScope);
})
);
it('should handle "=" bindings with same method names in Object.prototype correctly when not present', inject(
function($rootScope, $compile) {
var func = function() {
element = $compile(
'<div prototype-method-name-as-scope-var-a></div>'
)($rootScope);
};
expect(func).not.toThrow();
expect(element.find('span').scope()).toBe(element.isolateScope());
expect(element.isolateScope()).not.toBe($rootScope);
expect(element.isolateScope()['constructor']).toBe($rootScope.constructor);
expect(element.isolateScope()['valueOf']).toBeUndefined();
})
);
it('should handle "=" bindings with same method names in Object.prototype correctly when present', inject(
function($rootScope, $compile) {
$rootScope.constructor = 'constructor';
$rootScope.valueOf = 'valueOf';
var func = function() {
element = $compile(
'<div prototype-method-name-as-scope-var-a constructor="constructor" value-of="valueOf"></div>'
)($rootScope);
};
expect(func).not.toThrow();
expect(element.find('span').scope()).toBe(element.isolateScope());
expect(element.isolateScope()).not.toBe($rootScope);
expect(element.isolateScope()['constructor']).toBe('constructor');
expect(element.isolateScope()['valueOf']).toBe('valueOf');
})
);
it('should handle "@" bindings with same method names in Object.prototype correctly when not present', inject(
function($rootScope, $compile) {
var func = function() {
element = $compile('<div prototype-method-name-as-scope-var-b></div>')($rootScope);
};
expect(func).not.toThrow();
expect(element.find('span').scope()).toBe(element.isolateScope());
expect(element.isolateScope()).not.toBe($rootScope);
expect(element.isolateScope()['constructor']).toBe($rootScope.constructor);
expect(element.isolateScope()['valueOf']).toBeUndefined();
})
);
it('should handle "@" bindings with same method names in Object.prototype correctly when present', inject(
function($rootScope, $compile) {
var func = function() {
element = $compile(
'<div prototype-method-name-as-scope-var-b constructor="constructor" value-of="valueOf"></div>'
)($rootScope);
};
expect(func).not.toThrow();
expect(element.find('span').scope()).toBe(element.isolateScope());
expect(element.isolateScope()).not.toBe($rootScope);
expect(element.isolateScope()['constructor']).toBe('constructor');
expect(element.isolateScope()['valueOf']).toBe('valueOf');
})
);
it('should handle "&" bindings with same method names in Object.prototype correctly when not present', inject(
function($rootScope, $compile) {
var func = function() {
element = $compile('<div prototype-method-name-as-scope-var-c></div>')($rootScope);
};
expect(func).not.toThrow();
expect(element.find('span').scope()).toBe(element.isolateScope());
expect(element.isolateScope()).not.toBe($rootScope);
expect(element.isolateScope()['constructor']).toBe($rootScope.constructor);
expect(element.isolateScope()['valueOf']()).toBeUndefined();
})
);
it('should handle "&" bindings with same method names in Object.prototype correctly when present', inject(
function($rootScope, $compile) {
$rootScope.constructor = function() { return 'constructor'; };
$rootScope.valueOf = function() { return 'valueOf'; };
var func = function() {
element = $compile(
'<div prototype-method-name-as-scope-var-c constructor="constructor()" value-of="valueOf()"></div>'
)($rootScope);
};
expect(func).not.toThrow();
expect(element.find('span').scope()).toBe(element.isolateScope());
expect(element.isolateScope()).not.toBe($rootScope);
expect(element.isolateScope()['constructor']()).toBe('constructor');
expect(element.isolateScope()['valueOf']()).toBe('valueOf');
})
);
it('should not throw exception when using "watch" as binding in Firefox', inject(
function($rootScope, $compile) {
$rootScope.watch = 'watch';
var func = function() {
element = $compile(
'<div watch-as-scope-var watch="watch"></div>'
)($rootScope);
};
expect(func).not.toThrow();
expect(element.find('span').scope()).toBe(element.isolateScope());
expect(element.isolateScope()).not.toBe($rootScope);
expect(element.isolateScope()['watch']).toBe('watch');
})
);
});
@@ -2514,6 +2687,70 @@ describe('$compile', function() {
);
});
});
describe('multidir isolated scope error messages', function() {
angular.module('fakeIsoledScopeModule', [])
.directive('fakeScope', function(log) {
return {
scope: true,
restrict: 'CA',
compile: function() {
return {pre: function(scope, element) {
log(scope.$id);
expect(element.data('$scope')).toBe(scope);
}};
}
};
})
.directive('fakeIScope', function(log) {
return {
scope: {},
restrict: 'CA',
compile: function() {
return function(scope, element) {
iscope = scope;
log(scope.$id);
expect(element.data('$isolateScopeNoTemplate')).toBe(scope);
};
}
};
});
beforeEach(module('fakeIsoledScopeModule', function() {
directive('anonymModuleScopeDirective', function(log) {
return {
scope: true,
restrict: 'CA',
compile: function() {
return {pre: function(scope, element) {
log(scope.$id);
expect(element.data('$scope')).toBe(scope);
}};
}
};
});
}));
it('should add module name to multidir isolated scope message if directive defined through module', inject(
function($rootScope, $compile) {
expect(function() {
$compile('<div class="fake-scope; fake-i-scope"></div>');
}).toThrowMinErr('$compile', 'multidir',
'Multiple directives [fakeIScope (module: fakeIsoledScopeModule), fakeScope (module: fakeIsoledScopeModule)] ' +
'asking for new/isolated scope on: <div class="fake-scope; fake-i-scope">');
})
);
it('sholdn\'t add module name to multidir isolated scope message if directive is defined directly with $compileProvider', inject(
function($rootScope, $compile) {
expect(function() {
$compile('<div class="anonym-module-scope-directive; fake-i-scope"></div>');
}).toThrowMinErr('$compile', 'multidir',
'Multiple directives [anonymModuleScopeDirective, fakeIScope (module: fakeIsoledScopeModule)] ' +
'asking for new/isolated scope on: <div class="anonym-module-scope-directive; fake-i-scope">');
})
);
});
});
});
});
@@ -2777,6 +3014,25 @@ describe('$compile', function() {
}));
it('should handle consecutive text elements as a single text element', inject(function($rootScope, $compile) {
// No point it running the test, if there is no MutationObserver
if (!window.MutationObserver) return;
// Create and register the MutationObserver
var observer = new window.MutationObserver(noop);
observer.observe(document.body, {childList: true, subtree: true});
// Run the actual test
var base = jqLite('<div>&mdash; {{ "This doesn\'t." }}</div>');
element = $compile(base)($rootScope);
$rootScope.$digest();
expect(element.text()).toBe("— This doesn't.");
// Unregister the MutationObserver (and hope it doesn't mess up with subsequent tests)
observer.disconnect();
}));
it('should support custom start/end interpolation symbols in template and directive template',
function() {
module(function($interpolateProvider, $compileProvider) {
@@ -4358,6 +4614,41 @@ describe('$compile', function() {
});
it('should correctly assign controller return values for multiple directives', function() {
var directiveController, otherDirectiveController;
module(function() {
directive('myDirective', function(log) {
return {
scope: true,
controller: function($scope) {
return directiveController = {
foo: 'bar'
};
}
};
});
directive('myOtherDirective', function(log) {
return {
controller: function($scope) {
return otherDirectiveController = {
baz: 'luh'
};
}
};
});
});
inject(function(log, $compile, $rootScope) {
element = $compile('<my-directive my-other-directive></my-directive>')($rootScope);
expect(element.data('$myDirectiveController')).toBe(directiveController);
expect(element.data('$myOtherDirectiveController')).toBe(otherDirectiveController);
});
});
it('should get required parent controller', function() {
module(function() {
directive('nested', function(log) {
@@ -5358,8 +5649,13 @@ describe('$compile', function() {
expect(privateData.events.click).toBeDefined();
expect(privateData.events.click[0]).toBeDefined();
//Ensure the angular $destroy event is still sent
var destroyCount = 0;
element.find("div").on("$destroy", function() { destroyCount++; });
$rootScope.$apply('xs = null');
expect(destroyCount).toBe(2);
expect(firstRepeatedElem.data('$scope')).not.toBeDefined();
privateData = jQuery._data(firstRepeatedElem[0]);
expect(privateData && privateData.events).not.toBeDefined();
@@ -5380,9 +5676,7 @@ describe('$compile', function() {
testCleanup();
// The initial ng-repeat div is dumped after parsing hence we expect cleanData
// count to be one larger than size of the iterated array.
expect(cleanedCount).toBe(xs.length + 1);
expect(cleanedCount).toBe(xs.length);
// Restore the previous jQuery.cleanData.
jQuery.cleanData = currentCleanData;
+65
View File
@@ -1917,6 +1917,71 @@ describe('input', function() {
});
it('should parse exponential notation', function() {
var inputElm = helper.compileInput('<input type="number" name="alias" ng-model="value" />');
// #.###e+##
$rootScope.form.alias.$setViewValue("1.23214124123412412e+26");
expect(inputElm).toBeValid();
expect($rootScope.value).toBe(1.23214124123412412e+26);
// #.###e##
$rootScope.form.alias.$setViewValue("1.23214124123412412e26");
expect(inputElm).toBeValid();
expect($rootScope.value).toBe(1.23214124123412412e26);
// #.###e-##
$rootScope.form.alias.$setViewValue("1.23214124123412412e-26");
expect(inputElm).toBeValid();
expect($rootScope.value).toBe(1.23214124123412412e-26);
// ####e+##
$rootScope.form.alias.$setViewValue("123214124123412412e+26");
expect(inputElm).toBeValid();
expect($rootScope.value).toBe(123214124123412412e26);
// ####e##
$rootScope.form.alias.$setViewValue("123214124123412412e26");
expect(inputElm).toBeValid();
expect($rootScope.value).toBe(123214124123412412e26);
// ####e-##
$rootScope.form.alias.$setViewValue("123214124123412412e-26");
expect(inputElm).toBeValid();
expect($rootScope.value).toBe(123214124123412412e-26);
// #.###E+##
$rootScope.form.alias.$setViewValue("1.23214124123412412E+26");
expect(inputElm).toBeValid();
expect($rootScope.value).toBe(1.23214124123412412e+26);
// #.###E##
$rootScope.form.alias.$setViewValue("1.23214124123412412E26");
expect(inputElm).toBeValid();
expect($rootScope.value).toBe(1.23214124123412412e26);
// #.###E-##
$rootScope.form.alias.$setViewValue("1.23214124123412412E-26");
expect(inputElm).toBeValid();
expect($rootScope.value).toBe(1.23214124123412412e-26);
// ####E+##
$rootScope.form.alias.$setViewValue("123214124123412412E+26");
expect(inputElm).toBeValid();
expect($rootScope.value).toBe(123214124123412412e26);
// ####E##
$rootScope.form.alias.$setViewValue("123214124123412412E26");
expect(inputElm).toBeValid();
expect($rootScope.value).toBe(123214124123412412e26);
// ####E-##
$rootScope.form.alias.$setViewValue("123214124123412412E-26");
expect(inputElm).toBeValid();
expect($rootScope.value).toBe(123214124123412412e-26);
});
describe('min', function() {
it('should validate', function() {
+25
View File
@@ -1091,6 +1091,31 @@ describe('ngModel', function() {
}));
it('should be possible to extend Object prototype and still be able to do form validation',
inject(function($compile, $rootScope) {
Object.prototype.someThing = function() {};
var element = $compile('<form name="myForm">' +
'<input type="text" name="username" ng-model="username" minlength="10" required />' +
'</form>')($rootScope);
var inputElm = element.find('input');
var formCtrl = $rootScope.myForm;
var usernameCtrl = formCtrl.username;
$rootScope.$digest();
expect(usernameCtrl.$invalid).toBe(true);
expect(formCtrl.$invalid).toBe(true);
usernameCtrl.$setViewValue('valid-username');
$rootScope.$digest();
expect(usernameCtrl.$invalid).toBe(false);
expect(formCtrl.$invalid).toBe(false);
delete Object.prototype.someThing;
dealoc(element);
}));
it('should re-evaluate the form validity state once the asynchronous promise has been delivered',
inject(function($compile, $rootScope, $q) {
+57
View File
@@ -448,6 +448,43 @@ describe('ngOptions', function() {
});
it('should not watch non-numeric array properties', function() {
createSelect({
'ng-options': 'value as createLabel(value) for value in array',
'ng-model': 'selected'
});
scope.createLabel = jasmine.createSpy('createLabel').andCallFake(function(value) { return value; });
scope.array = ['a', 'b', 'c'];
scope.array.$$private = 'do not watch';
scope.array.$property = 'do not watch';
scope.array.other = 'do not watch';
scope.array.fn = function() {};
scope.selected = 'b';
scope.$digest();
expect(scope.createLabel).toHaveBeenCalledWith('a');
expect(scope.createLabel).toHaveBeenCalledWith('b');
expect(scope.createLabel).toHaveBeenCalledWith('c');
expect(scope.createLabel).not.toHaveBeenCalledWith('do not watch');
expect(scope.createLabel).not.toHaveBeenCalledWith(jasmine.any(Function));
});
it('should not watch object properties that start with $ or $$', function() {
createSelect({
'ng-options': 'key as createLabel(key) for (key, value) in object',
'ng-model': 'selected'
});
scope.createLabel = jasmine.createSpy('createLabel').andCallFake(function(value) { return value; });
scope.object = {'regularProperty': 'visible', '$$private': 'invisible', '$property': 'invisible'};
scope.selected = 'regularProperty';
scope.$digest();
expect(scope.createLabel).toHaveBeenCalledWith('regularProperty');
expect(scope.createLabel).not.toHaveBeenCalledWith('$$private');
expect(scope.createLabel).not.toHaveBeenCalledWith('$property');
});
it('should allow expressions over multiple lines', function() {
scope.isNotFoo = function(item) {
return item.name !== 'Foo';
@@ -1176,6 +1213,26 @@ describe('ngOptions', function() {
});
it('should not set view value again if the tracked property of the model has not changed when using trackBy', function() {
createSelect({
'ng-model': 'selected',
'ng-options': 'item for item in arr track by item.id'
});
scope.$apply(function() {
scope.selected = {id: 10, label: 'ten'};
});
spyOn(element.controller('ngModel'), '$setViewValue');
scope.$apply(function() {
scope.arr[0] = {id: 10, label: 'ten'};
});
expect(element.controller('ngModel').$setViewValue).not.toHaveBeenCalled();
});
it('should not re-render if a property of the model is changed when not using trackBy', function() {
createSelect({
+45 -2
View File
@@ -23,7 +23,7 @@ describe('Filter: orderBy', function() {
});
it('shouldSortArrayInReverse', function() {
it('should reverse collection if `reverseOrder` param is truthy', function() {
expect(orderBy([{a:15}, {a:2}], 'a', true)).toEqualData([{a:15}, {a:2}]);
expect(orderBy([{a:15}, {a:2}], 'a', "T")).toEqualData([{a:15}, {a:2}]);
expect(orderBy([{a:15}, {a:2}], 'a', "reverse")).toEqualData([{a:15}, {a:2}]);
@@ -116,7 +116,7 @@ describe('Filter: orderBy', function() {
});
it('should not reverse array of objects with no predicate', function() {
it('should not reverse array of objects with no predicate and reverse is not `true`', function() {
var array = [
{ id: 2 },
{ id: 1 },
@@ -126,6 +126,39 @@ describe('Filter: orderBy', function() {
expect(orderBy(array)).toEqualData(array);
});
it('should reverse array of objects with no predicate and reverse is `true`', function() {
var array = [
{ id: 2 },
{ id: 1 },
{ id: 4 },
{ id: 3 }
];
var reversedArray = [
{ id: 3 },
{ id: 4 },
{ id: 1 },
{ id: 2 }
];
expect(orderBy(array, '', true)).toEqualData(reversedArray);
});
it('should reverse array of objects with predicate of "-"', function() {
var array = [
{ id: 2 },
{ id: 1 },
{ id: 4 },
{ id: 3 }
];
var reversedArray = [
{ id: 3 },
{ id: 4 },
{ id: 1 },
{ id: 2 }
];
expect(orderBy(array, '-')).toEqualData(reversedArray);
});
it('should not reverse array of objects with null prototype and no predicate', function() {
var array = [2,1,4,3].map(function(id) {
@@ -151,6 +184,16 @@ describe('Filter: orderBy', function() {
null
]);
});
it('should sort array of arrays as Array.prototype.sort', function() {
expect(orderBy([['one'], ['two'], ['three']])).toEqualData([['one'], ['three'], ['two']]);
});
it('should sort mixed array of objects and values in a stable way', function() {
expect(orderBy([{foo: 2}, {foo: {}}, {foo: 3}, {foo: 4}], 'foo')).toEqualData([{foo: 2}, {foo: 3}, {foo: 4}, {foo: {}}]);
});
});
+1048 -735
View File
File diff suppressed because it is too large Load Diff
+7 -6
View File
@@ -1,12 +1,6 @@
/* global $LogProvider: false */
'use strict';
function initService(debugEnabled) {
return module(function($logProvider) {
$logProvider.debugEnabled(debugEnabled);
});
}
describe('$log', function() {
var $window, logger, log, warn, info, error, debug;
@@ -178,4 +172,11 @@ describe('$log', function() {
expect(errorArgs).toEqual(['abc', 'message\nsourceURL:123']);
});
});
function initService(debugEnabled) {
return module(function($logProvider) {
$logProvider.debugEnabled(debugEnabled);
});
}
});
+10
View File
@@ -1745,6 +1745,16 @@ describe('parser', function() {
expect(scope.$eval("0&&2")).toEqual(0 && 2);
expect(scope.$eval("0||2")).toEqual(0 || 2);
expect(scope.$eval("0||1&&2")).toEqual(0 || 1 && 2);
expect(scope.$eval("true&&a")).toEqual(true && undefined);
expect(scope.$eval("true&&a()")).toEqual(true && undefined);
expect(scope.$eval("true&&a()()")).toEqual(true && undefined);
expect(scope.$eval("true&&a.b")).toEqual(true && undefined);
expect(scope.$eval("true&&a.b.c")).toEqual(true && undefined);
expect(scope.$eval("false||a")).toEqual(false || undefined);
expect(scope.$eval("false||a()")).toEqual(false || undefined);
expect(scope.$eval("false||a()()")).toEqual(false || undefined);
expect(scope.$eval("false||a.b")).toEqual(false || undefined);
expect(scope.$eval("false||a.b.c")).toEqual(false || undefined);
});
it('should parse ternary', function() {
+8
View File
@@ -1643,6 +1643,14 @@ describe('q', function() {
});
describe('resolve', function() {
it('should be an alias of the "when" function', function() {
expect(q.resolve).toBeDefined();
expect(q.resolve).toEqual(q.when);
});
});
describe('optional callbacks', function() {
it('should not require success callback and propagate resolution', function() {
q.when('hi', null, error()).then(success(2), error());
+81
View File
@@ -57,6 +57,31 @@ describe('$aria', function() {
scope.$apply('val = true');
expect(element.attr('aria-hidden')).toBe('userSetValue');
});
it('should always set aria-hidden to a boolean value', function() {
compileElement('<div ng-hide="val"></div>');
scope.$apply('val = "test angular"');
expect(element.attr('aria-hidden')).toBe('true');
scope.$apply('val = null');
expect(element.attr('aria-hidden')).toBe('false');
scope.$apply('val = {}');
expect(element.attr('aria-hidden')).toBe('true');
compileElement('<div ng-show="val"></div>');
scope.$apply('val = "test angular"');
expect(element.attr('aria-hidden')).toBe('false');
scope.$apply('val = null');
expect(element.attr('aria-hidden')).toBe('true');
scope.$apply('val = {}');
expect(element.attr('aria-hidden')).toBe('false');
});
});
@@ -319,6 +344,20 @@ describe('$aria', function() {
scope.$apply('val = true');
expectAriaAttrOnEachElement(element, 'aria-disabled', 'userSetValue');
});
it('should always set aria-disabled to a boolean value', function() {
compileElement('<div ng-disabled="val"></div>');
scope.$apply('val = "test angular"');
expect(element.attr('aria-disabled')).toBe('true');
scope.$apply('val = null');
expect(element.attr('aria-disabled')).toBe('false');
scope.$apply('val = {}');
expect(element.attr('aria-disabled')).toBe('true');
});
});
describe('aria-disabled when disabled', function() {
@@ -510,6 +549,36 @@ describe('$aria', function() {
expectAriaAttrOnEachElement(element, 'aria-valuemin', 'userSetValue2');
expectAriaAttrOnEachElement(element, 'aria-valuemax', 'userSetValue3');
});
it('should update `aria-valuemin/max` when `min/max` changes dynamically', function() {
scope.$apply('min = 25; max = 75');
compileElement('<input type="range" ng-model="val" min="{{min}}" max="{{max}}" />');
expect(element.attr('aria-valuemin')).toBe('25');
expect(element.attr('aria-valuemax')).toBe('75');
scope.$apply('min = 0');
expect(element.attr('aria-valuemin')).toBe('0');
scope.$apply('max = 100');
expect(element.attr('aria-valuemax')).toBe('100');
});
it('should update `aria-valuemin/max` when `ng-min/ng-max` changes dynamically', function() {
scope.$apply('min = 25; max = 75');
compileElement('<input type="range" ng-model="val" ng-min="min" ng-max="max" />');
expect(element.attr('aria-valuemin')).toBe('25');
expect(element.attr('aria-valuemax')).toBe('75');
scope.$apply('min = 0');
expect(element.attr('aria-valuemin')).toBe('0');
scope.$apply('max = 100');
expect(element.attr('aria-valuemax')).toBe('100');
});
});
describe('announcing ngMessages', function() {
@@ -681,6 +750,18 @@ describe('$aria', function() {
});
});
describe('actions when bindRoleForClick is set to false', function() {
beforeEach(configAriaProvider({
bindRoleForClick: false
}));
beforeEach(injectScopeAndCompiler);
it('should not add a button role', function() {
compileElement('<radio-group ng-click="something"></radio-group>');
expect(element.attr('role')).toBeUndefined();
});
});
describe('actions when bindKeypress is set to false', function() {
beforeEach(configAriaProvider({
bindKeypress: false
+9
View File
@@ -20,6 +20,15 @@ describe('linky', function() {
expect(linky(undefined)).not.toBeDefined();
});
it('should be case-insensitive', function() {
expect(linky('WWW.example.com')).toEqual('<a href="http://WWW.example.com">WWW.example.com</a>');
expect(linky('WWW.EXAMPLE.COM')).toEqual('<a href="http://WWW.EXAMPLE.COM">WWW.EXAMPLE.COM</a>');
expect(linky('HTTP://www.example.com')).toEqual('<a href="HTTP://www.example.com">HTTP://www.example.com</a>');
expect(linky('HTTP://example.com')).toEqual('<a href="HTTP://example.com">HTTP://example.com</a>');
expect(linky('HTTPS://www.example.com')).toEqual('<a href="HTTPS://www.example.com">HTTPS://www.example.com</a>');
expect(linky('HTTPS://example.com')).toEqual('<a href="HTTPS://example.com">HTTPS://example.com</a>');
});
it('should handle www.', function() {
expect(linky('www.example.com')).toEqual('<a href="http://www.example.com">www.example.com</a>');
});