Compare commits

...

160 Commits

Author SHA1 Message Date
Martin Staffa 9fa73cb4e7 fix(select): use strict compare when removing option from ctrl
Otherwise, if the removed option was the empty option (value ''),
and the currently selected option had a value of 0, the select
would think that the currently selected option had been removed,
causing the unknown option to be added again.

Fixes #9714
Fixes #10115
Closes #10203
2014-12-01 19:54:14 +01:00
Pawel Kozlowski f6458826ac fix($emplateRequest): propagate rejection reason when ignoreRequestError flag is set
Closes #10266
2014-12-01 19:50:13 +01:00
Pawel Kozlowski ab2531143e refactor($templateRequest): simplify filtering out of transform functions
Closes #10264
2014-12-01 19:31:53 +01:00
Shahar Talmi 9a83f9d2fa fix(ngMock): annotate $RootScopeDecorator
Fixes #10273
Closes #10275
Closes #10277
2014-12-01 19:25:43 +01:00
Peter Bacon Darwin 3f07eb227d test($location): fix test of {rewriteLinks:false}
The test for not rewriting links was invalid and just happened to be
passing by chance (false-positive).
2014-12-01 13:39:27 +00:00
Peter Bacon Darwin 446e5669a1 test($location): fix typo 2014-12-01 13:38:47 +00:00
Peter Bacon Darwin b264be40bc revert: fix(Angular.js): toKeyValue is not serializing null values
This commit contained broken tests and was not ready to be merged.

(reverted from commit 814c9847e8)
2014-12-01 12:42:18 +00:00
Josh Kurz 814c9847e8 fix(Angular.js): toKeyValue is not serializing null values
Signed-off-by: Josh Kurz <jkurz25@gmail.com>
2014-11-30 20:35:14 -05:00
Jesse Palmer dde613f18e chore(docs): fix dangling links warning in $http API
Closes #10270
2014-11-30 12:50:41 +01:00
Chris Tanseer e6a2527cdf docs($rootElement): fix minor grammatical errors
Closes #10269
2014-11-29 23:19:32 +01:00
Caitlin Potter d0351c4803 style(ngHref): make jscs happy ;-; 2014-11-28 22:28:39 -06:00
Sagar Ranglani b2b6d74ae5 docs(ngHref): fix poor paragraph construction
It was bad.

In order to improve the docs, the inclusion of the last sentence would help define `ngHref` well.

Closes #10254
2014-11-28 22:09:18 -06:00
Lucas Galfaso 30694c8027 fix(select): fix several issues when moving options between groups
* When an option was moved to a previous group, the group that
loose the option would remove the label from the controller
* When an entire option group was removed, the options in the
group were mot removed from the controller

Closes #10166
2014-11-27 20:40:43 +00:00
Ates Goral 655ac6474b docs(guide/ie): fixed minor typo
Closes #10251
2014-11-27 21:14:01 +01:00
Eric Theise e5a9b265ba docs(tutorial): fix grammar
Should either be "a different version" or "different versions";
this goes with the latter.

Closes #10249
2014-11-27 19:02:31 +01:00
Danny Shekhtman e2b9eccde0 docs(guide): fix typo
Closes #10245
2014-11-27 08:53:05 +01:00
Sugan Krishnan 9b3d9656a6 docs($compile): fix grammar
Closes #10231
2014-11-27 08:52:13 +01:00
Caitlin Potter 1e6a5b29a6 style(*): IE9 does still have issues with apply on some native functions
This partially reverts 8f05ca5552

Related to #10242
2014-11-26 21:51:31 +00:00
Caitlin Potter 8f05ca5552 style(*): IE is a real browser, and chakra is pretty solid
- IE9+ do not have issues with Function.prototype.bind() on builtin fns (asked Brian Terlson)
  (NOTE: there may still be corner cases where builtins will not have `bind()` --- this may
  need to be reverted on complaint).
- HTMLScriptElement#text is an IDL-spec'd attribute, and we use it in all cases --- so the
  comment was sort of nonsense.
- The value of `msie` does not depend on whether the user is using a "real" browser or not.

Closes #10242
2014-11-26 14:36:33 -06:00
Lucas Galfaso 2ec8d1ffc0 fix(linky): encode all double quotes when serializing email addresses
When encoding a URL or an email address, then escape all double quotes

Closes #10090
2014-11-26 21:35:11 +01:00
Shahar Talmi 08cd5c19c7 fix(ngMock): respond did not always take a statusText argument
minor fix so that `respond` can take a `statusText` argument
even when having status 200 by default

Closes #8270
2014-11-26 20:03:23 +01:00
Or Neeman 41dc7d5ebd docs(ngRoute): clarify JSDoc for caseInsensitiveMatch
Closes #10220
2014-11-26 19:05:53 +01:00
Georgios Kalpakas 0caa5ad83f docs(CHANGELOG): remove * component
Closes #10222
2014-11-26 19:04:24 +01:00
Mathew Foscarini 5d36353bc9 docs($compile): fix grammar
Closes #10204
2014-11-25 19:51:33 +01:00
Guillaume Pannatier 719d5c5fa5 fix($httpBackend): allow canceling request with falsy timeoutId
httpBackend with ngMock browser.defer could never cancel the first deferredFn
because the timeoutId returned by defer for the first fn is a zero value.
Compare timeoutId with undefined fix this issue.

Closes #10177
2014-11-25 19:17:57 +01:00
Guillaume Pannatier 266da34098 test($httpBackend): use browser.defer mock instead of fakeTimeout 2014-11-25 19:17:01 +01:00
Lucas Galfaso 9474ec120a docs(CHANGELOG): add v1.3.4 changes 2014-11-25 00:05:18 +01:00
Pawel Kozlowski 09a9832358 fix(Angular): properly get node name for svg element wrapper
Fixes #10078
Closes #10172
2014-11-24 22:11:00 +00:00
Jason Bedard bf6a79c348 perf(*): use Object.create instead of creating temporary constructors
Closes #10058
2014-11-23 22:37:08 +00:00
Lucas Galfaso 8ee8ffeba0 fix(linky): encode double quotes when serializing email addresses
Email addresses can (under certain restrictions) include double quote
characters. See http://tools.ietf.org/html/rfc3696#section-3.

For example, `"Jo Bloggs"@abc.com` is a valid email address.

When serializing emails to the `href` attribute of an anchor element,
we must HTML encode these double quote characters. See
http://www.w3.org/TR/html-markup/syntax.html#syntax-attr-double-quoted

This commit does not attempt to improve the functionality (i.e. regex)
that attempts to identify email addresses in a general string.

Closes #8945
Closes #8964
Closes #5946
Closes #10090
Closes #9256
2014-11-23 21:58:18 +00:00
Peter Bacon Darwin 42d09f1772 docs(NgModelController): clarify the value parameter for $isEmpty 2014-11-23 15:24:36 +00:00
Martin Staffa 8692f87a46 fix(input): set ngTrueValue on required checkbox
Fixes #5164
2014-11-23 15:08:39 +00:00
Peter Bacon Darwin 40406e2f22 fix(input[date]): do not use $isEmpty to check the model validity 2014-11-23 15:08:39 +00:00
Peter Bacon Darwin 4644c5840f revert: fix(input): always pass in the model value to ctrl.$isEmpty
This commit tried to create consistency by ensuring that `$isEmpty` is not
called on both model and view values but it chose to only use `$modelValue`,
which is not actually correct.

`$isEmpty` is designed to compute whether the `$viewValue` is empty. In
practice this is the only part of the parse/format system that the
directive has control over. We can't rely on the `$modelValue` being in
any particular format since other directives can add in their own formatters
and parsers to completely change this.

(reverted from commit 3e51b84bc1)
2014-11-23 15:08:39 +00:00
thorn0 addb4bdd57 chore(docs): regroup version selector options into major branches and latest
Before this change we grouped by the  discontinued stable/unstable distinction.

Closes #10053
2014-11-23 13:26:33 +00:00
Gonzalo Ruiz de Villa 7496e8e5b7 refactor(*): combine sequence of .push() into one statement
Closes #10192
2014-11-23 09:38:36 +01:00
Dustin e9b9421cdb fix ($http): throw error when string URL is provided 2014-11-22 15:34:55 -08:00
Rado Kirov 7a374691b9 docs(injector): adds a missing backtick and reformat list.
Closes #10189
2014-11-22 14:48:02 -08:00
Shang Heng (Shawn) Wei 3be6835e0f docs($location): add examples for some methods
Closes #9754
2014-11-22 14:16:57 -08:00
shwei a3d79775e1 refactor(ngForm): Remove checking event.preventDefault and calling event.returnValue. Related to issue #4557. Worked with @bullwrinkle on this. 2014-11-22 14:13:12 -08:00
Rado Kirov 920b595080 doc(injector): adds strictDi documentation to appropriate methods.
Closes #9624
2014-11-22 14:01:37 -08:00
Jack Franklin 3109342679 docs(guide/unit-testing): improve unit testing guide
This commit adds to the unit testing guide:

- an explicit section on additional libraries: Karma, Jasmine and
  angular-mocks and link to the docs for those projects too. Explain the
  benefit and use case for each of these libaries
- fully featured test examples and add more documentation
  around them, in particular the controller test
- a clear separation between the section on principles of testing
  and the actual tutorial on writing a test

Closes #8220
2014-11-22 13:47:41 -08:00
Pawel Kozlowski aa01be8b2c test($http): parsing headers with multiple values
Closes #9473

Closes #10176
2014-11-22 22:11:59 +01:00
Brian Ford bb4d3b73a1 fix(ngModelOptions): preserve context of getter/setters
Many thanks to @NevilleS and @jbedard for collaborating with me on a solution to this!

Closes #9394
Closes #9865

BREAKING CHANGE: previously, ngModel invoked getter/setters in the global context.

For example:

```js
<input ng-model="model.value" ng-model-options="{ getterSetter: true }">
```

would previously invoke `model.value()` in the global context.

Now, ngModel invokes `value` with `model` as the context.

It's unlikely that real apps relied on this behavior. If they did they can use `.bind` to explicilty
bind a getter/getter to the global context, or just reference globals normally without `this`.
2014-11-22 12:36:12 -08:00
Wesley Cho 6dbd606ad7 fix($locale): Allow currency filter to fall back to maxFrac from locale
- Modify default fallback to `NUMBER_FORMATS.PATTERNS[1].maxFrac`

- Remove unnecessary resetting

Closes #10179
2014-11-22 12:29:06 -08:00
thorn0 764fa869dd fix($browser): allow chaining url() calls in setter mode
Closes #10157
2014-11-22 16:48:54 +01:00
Neville Samuell 7812dfcee8 fix($location): allow empty string URLs to reset path, search, and hash
Currently, providing '' to $location#url will only reset the hash, but otherwise has no effect. This
change brings the behaviour of $location#url more inline with window.location.href, which when
assigned to an empty string loads the page's base href.

Before:
$location.url() // http://www.example.com/path
$location.url('') // http://www.example.com/path

After:
$location.url() // http://www.example.com/path
$location.url('') // http://www.example.com

Fixes #10063

Closes #10064
2014-11-22 16:30:41 +01:00
Georgios Kalpakas 00b623e86b docs(ngMaxlength): document what happens on negative/non-numeric values
Closes #9998
2014-11-22 16:00:44 +01:00
Georgios Kalpakas 92f87b1142 fix(ngMaxlength): ignore maxlength when not set to a non-negative integer
This makes the behaviour of maxlength/ngMaxlength more inline with the
spec.

Closes #9874
2014-11-22 16:00:44 +01:00
Georgios Kalpakas 5c1fdff691 feat(ngMaxlength): add support for disabling max length limit
Previously, setting the maxlength to a negative number, would make all
input values invalid (since their length should be less than maxlength,
which is impossible).
This commit changes the behaviour of maxlength/ngMaxlength, effectively
disabling the maxlength validation (always returning true) when maxlength
is set to a negative number. This is more inline to how the HTML5
`maxlength` attribute works (both in browsers and according to the spec:
http://dev.w3.org/html5/spec-preview/attributes-common-to-form-controls.html#attr-fe-maxlength).

Related to #9874
Closes #9995
2014-11-22 16:00:43 +01:00
Georgios Kalpakas 891acf4c20 fix($route): fix redirection with optional/eager params
Previously, when (automatically) redirecting from path that fetured a
trailing slash and optional or "eager" parameters, the resulting path
would (incorrectly) contain the special characters (`?`,`*`) along with
the parameter values.

Closes #9819

Closes #9827
2014-11-22 15:34:16 +01:00
Georgios Kalpakas 93552fed1c test($route): fix test names 2014-11-22 15:34:16 +01:00
Peter Bacon Darwin b5fbd6a2f6 chore(favicon): provide retina friendly favicon
See https://github.com/angular/angularjs.org/pull/143
2014-11-22 10:54:28 +00:00
Peter Bacon Darwin 5c43b94fc4 chore(i18n): update locale to CLDR v26 2014-11-21 14:42:44 +00:00
Georgios Kalpakas 7dd94b9595 docs(ngAnimate.$animate): fix classes during the various animation phases
Closes #10124
2014-11-21 13:57:35 +00:00
Peter Bacon Darwin 95f8a8bab0 style(ngBind): remove trailing whitespace 2014-11-21 13:51:04 +00:00
Tony Rizko dc5bba8615 docs(ngBindHtml): fix awkward wording
Fixes #10097
Closes #10129
2014-11-21 13:29:16 +00:00
Peter Bacon Darwin 16c8f29ef6 docs(ngShow/ngHide): add spaces to improve readability of CSS
Closes #10101
2014-11-21 11:08:22 +00:00
Pawel Kozlowski d3fb8dd776 docs($filter): clarify what is a valid filter name
Also updates the CHANGELOG to add info about this breaking some
previous invalid uses of $filter

Closes #10054
Closes #10131
2014-11-21 11:04:24 +00:00
Caitlin Potter 95e03bce7e style($http): make jscs happy 2014-11-21 00:26:39 -05:00
Dustin Chilson 5388ca5710 docs($http): describe how to remove a header on a per request basis
Closes #10144
2014-11-21 00:26:17 -05:00
Brian Ford 1b275fb00e chore(scripts): fix 1.2.x tag name 2014-11-20 15:02:34 -08:00
Lucas Galfaso 1c68d00fbf docs(ngAnimate): Fix typo
The ngAnimate makes reference to a function `$animateProvider.classNamePrefix`
that does not exist, the correct function is `$animateProvider.classNameFilter`

Closes #10142
2014-11-20 23:34:37 +01:00
Brian Ford 158241e212 chore(scripts): publish 1.2.x releases to npm with correct tag 2014-11-20 14:31:58 -08:00
AlexChan eab271876c fix(input): call $setTouched in blur asynchronously if necessary
If the model is blurred during an apply it should trigger
$setTouched asynchronously.

Fixes #8762
Fixes #9808
Closes #10014
2014-11-20 23:01:43 +01:00
Peter Bacon Darwin 637d3b47d1 docs($httpProvider): add info about defaults.cache
Closes #10134
2014-11-20 21:25:38 +00:00
Martin Staffa 8ac369e829 docs(ngModelController): update wordings, add more general info
The wordings in setDirty etc. were specific to inputs, but ngModelCtrl
is agnostic to this and the preferred term is 'control'. I also
added some more info about this to the description, and linked to
the example that now lives at the bottom of the page.
2014-11-20 22:19:02 +01:00
Jeff Cross 5c611e898a docs(CHANGELOG): update changelog with 1.2.27 2014-11-20 10:28:02 -08:00
Peter Bacon Darwin dc9775da96 doc(ngModelController): move example below members 2014-11-19 21:31:56 +00:00
IShotTheSheriff e8941c0fe5 feat(ngModelController): add $setDirty method
- extract existing functionality to public method: $setDirty
- add tests to corresponding changes
- refactor code to use extracted method

Closes #10038
Closes #10049
2014-11-19 20:07:07 +01:00
Toilal bb16759f0b docs(jqLite): clarify that Debug Data must be enabled for scope/isolateScope methods
Closes #9515
Closes #10123
2014-11-19 19:54:15 +01:00
Georgios Kalpakas 2b41a5868a feat(ngPluralize): add support for count to be a one-time expression
Closes #10004
2014-11-18 23:49:24 +01:00
Jamshid Afshar 637c020f82 fix($http): return empty headers, ignore properties in Object prototype
Fix response headers with an empty value Empty response header values are legal
(http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html).

The headers getter fn will now only return null if the header property is not an own property.

Closes #7779
Closes #10113
Closes #10091
2014-11-18 16:37:38 -05:00
Georgios Kalpakas f7fde935d5 docs($location): fix method-name and typos in ihshprfx error
Closes #10106
2014-11-18 19:05:45 +01:00
Peter Bacon Darwin 5f552896ab chore(docs): update to dgeni-packages 0.10.7
This update to dgeni-packages fixes some trimIndentation issues

Closes #10101
2014-11-18 14:24:22 +00:00
Peter Bacon Darwin d5968c7853 docs(CHANGELOG): add 1.3.3 changes 2014-11-18 08:23:38 +00:00
PatrickJS 4f4ff5f31b fix(NgModelController): typo $rawModelValue -> $$rawModelValue 2014-11-18 08:08:47 +01:00
Martin Staffa e3764e30a3 fix(ngModel): don't run parsers when executing $validate
Previously, $validate would execute the parsers to obtain a
modelValue for validation. This was necessary, because a validator
that is called outside of model / view update (e.g. from an observer)
otherwise might only an undefined modelValue, because a previous
view update has found a validation $error and set the model
to undefined (as is tradition in angular)

This is problematic as validators that are run immediately after
the ngModelController initializes would parse the modelValue
and replace the model, even though there had been no user input.

The solution is to go back to an older design: the ngModelController
will now internally record the $$rawModelValue. This means a model
or view update will store the set / parsed modelValue regardless
of validity, that is, it will never set it to undefined because of
validation errors.

When $validate is called, the $$rawModelValue will passed to the
validators. If the validity has changed, the usual behavior is kept:
if it became invalid, set the model to undefined, if valid,
restore the last available modelValue - the $$rawModelValue.

Additionally, $validate will only update the model when the validity
changed. This is to prevent setting initially invalid models other
than undefined to undefined (see #9063)

Fixes: #9063
Fixes: #9959
Fixes: #9996
Fixes: #10025

Closes: #9890
Closes: #9913
Closes: #9997
Closes: #10048
2014-11-17 20:26:27 +01:00
Martin Staffa c9899c53ac test(ngModel): group validation tests 2014-11-17 20:26:17 +01:00
Jeff Cross 4d4e6036a9 chore(docs): add favicon to docs app 2014-11-17 09:32:21 -08:00
samuel durand 4d885cbd62 docs($anchorScrollProvider): remove repeated word
Closes #10093
2014-11-17 14:44:23 +00:00
Brian Westrich eec2020518 docs(tutorial/step-5): include sort and filter in json view experiment
Closes #10082
2014-11-17 14:33:50 +00:00
Brian Westrich 2901c53f42 docs(tutorial/step-4): "unknown" option is actually blank
The name 'unknown' doesn't appear as a choice, the new choice is just blank.
Side note: once I choose one of the non-blank options, I no longer see the blank option.

Closes #10079
2014-11-17 13:47:47 +00:00
Peter Bacon Darwin e80053d91f fix($rootScope): handle cyclic references in scopes when creating error messages
Use the new private function `stringify` to convert scope values to strings,
since this can cope with cyclic references and other oddities.

Closes #10085
2014-11-17 13:38:04 +00:00
Peter Bacon Darwin fa12c3c86a fix(ngRepeat): support cyclic object references in error messages
Now that `minErr` can cope with objects that cannot be normally stringified
to JSON, just pass the error arguments straight through without trying to
stringify them first.

Closes #9838
Closes #10065
Closes #10085
2014-11-17 13:37:58 +00:00
Peter Bacon Darwin cf43ccdf9b fix(minErr): stringify non-JSON compatible objects in error messages
Fix the JSON stringification to output a more meaningful string when an
object cannot be normally converted to a JSON string, such as when the
object contains cyclic references that would cause `JSON.stringify()`
to throw an error.

Closes #10085
2014-11-17 13:37:52 +00:00
Shahar Talmi a9352c19ce feat($location): allow to location to be changed during $locationChangeStart
Closes #9607
Closes #9678
2014-11-15 23:25:21 +00:00
Jakub Hampl 6f19a6fd33 fix($http): don't parse single space responses as JSON
Closes #9907
2014-11-15 12:43:24 +01:00
Kent C. Dodds f30163e63a docs(nav): highlight current nav-index-listing
Color the current nav-index-listing item dark red
to make it easier to know where you are.

Closes #9970
Closes #9974
2014-11-15 11:30:23 +01:00
Marcy Sutton 5b23bc9b07 docs(ngAria): Add Usage Details and Examples
Closes #10031
2014-11-14 18:08:42 -05:00
Marcy Sutton 9d1e87a3f1 docs(guide/accessibility): Content updates
Also includes new section on ngMessages
2014-11-14 18:06:59 -05:00
Peter Bacon Darwin 6604c23614 fix(select): ensure the label attribute is updated in Internet Explorer
Only changing the `<option>` text value is not enough to trigger a render
change in IE. We need to explicit update the `label` property too.

Closes #9621
Closes #10042
2014-11-14 21:17:28 +00:00
Peter Bacon Darwin 195deca6e2 test(select): refactor option elements expectations to use toEqualOption matcher
By using a new matcher our tests become less brittle with respect to unimportant
extra attributes.
2014-11-14 21:16:42 +00:00
Caitlin Potter 85eb9660ef fix(ngPattern): match behaviour of native HTML pattern attribute
From https://html.spec.whatwg.org/multipage/forms.html#attr-input-pattern

> The compiled pattern regular expression, when matched against a string, must have its start
anchored to the start of the string and its end anchored to the end of the string.

Closes #9881
Closes #9888
2014-11-14 20:18:57 +00:00
David Stensland d81ff8885b fix(ngMock): call $interval callbacks even when invokeApply is false
Make ngMock.$interval behave like ng.$interval

Closes #10032
2014-11-14 11:42:24 -05:00
Caitlin Potter eca14d98a4 chore($q): make jscs happy
jscs loves to be happy and does not love trailing whitespace.
2014-11-14 11:11:39 -05:00
Bernie Telles 22ecbc50f4 docs($q): explain what the $q service does in description
Explain what the $q service does in description, instead of origin document.

The original explanation was less accessible to people new to promises and JS in general.

Closes #10056
2014-11-14 11:09:37 -05:00
Chatchavan Wacharamanotham 7f857e44a2 docs(guide/compiler): replaced 'locals' with 'scope'
In "Understanding How Scopes Work with Transcluded Directives" section, a text referred to an
obsolete 'locals' instead of 'scope'.

Closes #10018
2014-11-14 11:03:24 -05:00
Caitlin Potter be6920b356 test(orderBy): add test cases for ordering array-like objects
Closes #10060
2014-11-14 10:02:23 -05:00
Karol Wypchło 8eabc5463c perf(orderBy): copy array with slice instead of for loop
Use array slice method to copy entire array instead of a for loop
http://jsperf.com/new-array-vs-splice-vs-slice/54

Closes #9942
2014-11-14 09:34:16 -05:00
Anton Savchenko 14ff529fbb refact(angular.bind): use concat() rather than duplicating code
Closes #4200
2014-11-13 13:33:24 +00:00
Jason Bedard fbad280570 refactor($parse): separate tokenizing vs parsing more
Fixes `a.b` and `a .b` generating different getterFns
Fixes `{'': ...}` turning into `{"''": ...}`
Fixes `{.: ...}` parsing as `{'.': ...}` instead of throwing
Fixes #9131
2014-11-12 23:36:23 +01:00
Pawel Kozlowski 8b775a0d58 docs(guide/expressions): clarify regexp literals usage in expressions
Closes #10026

Closes #10030
2014-11-12 22:50:21 +01:00
Pawel Kozlowski 0bbc6ee481 docs($route): fix description of the caseInsensitiveMatch property
Closes #10028
2014-11-12 21:02:24 +01:00
Blaise Kal 381b185117 docs(guide/expressions): replace curly quotes with straight quotes in code example
REAL QUOTES HAVE CURVES

Closes #10017
2014-11-12 13:50:09 -05:00
Henrique Ramos Limas fa0d8c47c3 docs($controller): mention "controller as" syntax
Closes #10011
2014-11-12 08:13:43 +00:00
PatrickJS 7e233eb58a docs($q): missing finally notifyCallback API
finally allows for notifyCallback and is missing in the Docs
https://github.com/angular/angular.js/blob/v1.3.2/src/ng/q.js#L288

Closes #10010
2014-11-12 08:04:56 +00:00
Caitlin Potter b7afd11d26 refactor($parse): don't use bind-once interceptor for non-bind-once expressions
Side-effects:
  - Logic for allOrNothing watches now lives in $interpolate rather than $parse

Credit to @jbedard for idea to remove $watch interceptors craziness from $interpolate. Even though
it technically didn't actually work, it was worth a shot, and helped clean things up a bit. Go team!

Closes #9958
Closes #9961
2014-11-11 20:29:36 -05:00
Georgios Kalpakas 8582088b36 docs($q): remove IE8-specific notice
Closes #10008
2014-11-11 21:03:51 +01:00
Pawel Kozlowski 0db573b749 feat($routeProvider): allow setting caseInsensitiveMatch on the provider
Fixes #6477

Closes #9873
2014-11-11 20:20:16 +01:00
codef0rmer 5e78af769e docs(guide/Index): add book AngularJS UI Development
Matthias and I wrote a book on AngularJS which might be helpful for Angular developers.

Merci~!

Closes #9971
2014-11-11 14:13:28 -05:00
AlexChan 804e75045c docs(ngModel.NgModelController) use $evalAsync instead of $apply for event handling
Have the apply called safely during events by using `$evalAsync` rather than `$apply`
This will help ensure that an apply for a user directive is not called during a digest cycle.

Closes #9891
2014-11-11 13:59:17 -05:00
Dim ebc3b7b1c3 docs(minerr/unpr): fix code example
Closes #10000
2014-11-11 12:35:19 +01:00
Andreas Fischer 830846f664 docs(guide/Modules): missing "a" in "a collection"
Closes #10001
2014-11-11 12:24:30 +01:00
yarsh 0462ee6659 docs(ngModelOptions): minor grammer error
Closes #9928
2014-11-11 10:20:32 +01:00
rsperberg 9cc6835819 docs(guide/Conceptual Overview): change "a" to "an" before "ng-controller"
Closes #9895
2014-11-10 20:41:04 +01:00
inphovore ee1fc1dc13 docs(guide/Bootstrap): batarang link correction
Closes #9869
2014-11-10 19:42:20 +01:00
Nicholas Albion 41b36e689c docs($compile): bindToController clarification
It was not clear that `bindToController` is a boolean.

Closes #9835
2014-11-10 19:19:05 +01:00
Georgios Kalpakas 52545e5a74 docs($browser): minor docs fixes
Remove obsolete `XHR` param from the docs and correct
`$log` param description.

Closes #9810
2014-11-10 19:07:15 +01:00
Justin 2abea7514a docs(select): minor markdown syntax fix
Half the sentence was being highlighted as code.

Closes #9983
2014-11-10 12:05:46 -05:00
Martin Staffa f157d02793 docs(ngModelController): clarify parse errors
Closes #9952
2014-11-09 23:31:27 +01:00
Rouven Weßling 77d8ae1d45 refactor($location) Remove unused variables
These were left around in 736c8fbbae

Closes #9482
2014-11-09 16:18:00 +01:00
Henry Zhu e21b6ff3ff style(*): add rule requireSpacesInConditionalExpression
Closes #9973
2014-11-09 15:51:01 +01:00
Henry Zhu 06016bb12c style(*): add rules requireSpace(After|Before)BinaryOperators 2014-11-09 15:51:01 +01:00
Arjunkumar 50e72fcae1 docs(guide/migration): typo fix
spell check propery to property

Closes #9937
2014-11-08 14:56:39 +01:00
micellius b84e62bd28 docs(CHANGELOG): ohmygosh they're different ohmygoshohmygosh
Align versioning format

Closes #9968
2014-11-08 08:11:51 -05:00
Igor Minar b6fd184a93 docs(CHANGELOG): add a security note to the 1.3.2 log 2014-11-07 17:09:44 -08:00
Caitlin Potter 1db9e617ce docs(CHANGELOG): slight fixup 2014-11-07 14:11:10 -05:00
Caitlin Potter 0918f146e4 docs(CHANGELOG): add v1.3.2 changes 2014-11-07 14:09:32 -05:00
Brian Ford 6dfd938bbc docs(guide/index): link to security 2014-11-07 10:32:10 -08:00
Brian Ford 4f5a60bcca docs($parse): formatting, link to security docs 2014-11-07 10:32:10 -08:00
Brian Ford e593939411 docs(security): add security doc 2014-11-07 10:32:10 -08:00
Martin Staffa 9e305948e4 fix(select): use strict comparison for isSelected with selectAs
Closes #9639
Closes #9949
2014-11-07 13:22:01 -05:00
Lucas Galfaso ed99821e4d fix($parse): stateful interceptors override an undefined expression
When using `$parse` with a stateful interceptor and the expression
is `undefined`, then return the result from the interceptor

NOTE from Igor: this is not the best solution. We need to refactor
this and one-time + $interpolate code to properly fix this. @caitp
is on it. See discussion in the PR.

Closes #9821
Closes #9825
2014-11-07 10:17:32 -08:00
Chirayu Krishnappa e057a9aa39 fix($parse, events): prevent accidental misuse of properties on $event 2014-11-06 19:30:48 -08:00
Chirayu Krishnappa e676d642f5 fix($parse): add quick check for Function constructor in fast path 2014-11-06 19:30:48 -08:00
Lucas Galfaso 288b531626 docs(CHANGELOG): Document breaking change from 23bc92b1
Document the breaking change from 23bc92b1

Closes #9947
2014-11-06 15:27:44 -05:00
rsperberg 7a4df50480 docs(guide/concepts): spell "Angular" with cap "A", fix typos
In these two instances, Angular was spelled with a lower-case "a." All occurrences should be spelled
consistently.

Compound adjectives preceding the noun they modify should generally be hyphenated (cf Chicago Manual
of Style, 6.40), e.g., "so-called directives."

Closes #9896
2014-11-06 14:16:16 -05:00
Adir 6550198003 docs(guide,tutorial): fix outdated Protractor API link
Link to the gh-pages deployment of protractor docs, it's much easier on the eyes.

Closes #9946
2014-11-06 13:57:50 -05:00
Marcy Sutton c6909ed144 docs(guide/accessibility): Add in-depth guide
Closes #9930
2014-11-06 10:17:37 -05:00
Marcy Sutton 187e43185d feat(ngAria): announce ngMessages with aria-live
By including the `ngAria` module, `ngMessages` will automatically include the aria-live
attribute with an assertive voice, allowing validation messages to be spoken throuhg a
screenreader.

Closes #9834
2014-11-05 17:50:18 -05:00
Peter Bacon Darwin 91834bcf37 test($compile): use ngMock.Scope.$countChildScopes() 2014-11-05 20:41:51 +00:00
Dave Longley 841c090755 fix($compile): do not rebind parent bound transclude functions
The `$compile` public API documentation indicates that
a transclude function may be passed as a second parameter, but
it was not clear that this is **not** the same function that is given
to directive link functions as the `transcludeFn` parameter.

We would like to be able to pass in a transclude function the public
linking function that is returned from `$compile` if we wish to, for
example, use lazy compilation inside a directive.

Doing so, however, highlighted two bugs:

* First, the transclude function would get rebound, incorrectly, changing
its scope from its original binding.
* Second, the `containingScope` would not be updated and the wrong
`$parent` would be assigned to the `transcludedScope` resulting in a
memory leak.

This patch fixes both of these issues.

It also converts various private optional positional parameters on `publicLinkFn`
into an `options` parameter, which is an object hash. The new `options`
parameter is documented as part of the public API.

Thanks to @lgalfaso, @tbosch, and @petebacondarwin.

Closes #9413
2014-11-05 20:41:51 +00:00
Kent C. Dodds da960544f1 docs(guide/Running in Production): ng-strict-di
Adding note about Strict DI mode.

Closes #9908
2014-11-05 14:43:13 -05:00
Igor Minar 74981c9f20 feat(ngMock): decorator that adds Scope#$countChildScopes and Scope#$countWatchers
When writing tests it's often useful to check the number of child scopes
or watchers within the current current scope subtree. Common use-case for advanced
directives is to test that the directive is properly cleaning up after itself. These
new methods simplify writing assertions that verify that child scopes were properly
destroyed or that watchers were deregistered.

Closes #9926
Closes #9871
2014-11-05 13:23:19 -05:00
danielmbarlow dc4b06559f docs(tutorial/step_12): added 'see phone-detail change'
This one caught me out for a while because, despite the note underneath, I didn't notice the addition
of <div class="phone-images"> and it's repeater until later.

Closes #9924
2014-11-05 12:13:57 -05:00
danielmbarlow 56138bdd63 docs(tutorial/step_12): small change to overview
The bullet points at the beginning of the article were a little hard to understand because they
didn't follow the grammatical form of the preceding articles. I hope these small modifications make
it a little easier for someone else to read.

Closes #9922
2014-11-05 09:08:56 -05:00
danielmbarlow 0ccc4fcd89 docs(tutorial/step_05): explain need for $httpBackend.flush in tests
There is an excellent explanation for the need for this in the documentation that may be helpful to
tutorial users, so I added a link to it.

Closes #9919
2014-11-05 08:36:37 -05:00
Julie Ralph b7e2b01bb5 chore(ci): update protractor to version 1.4.0 2014-11-04 17:16:19 -08:00
Tobias Gesellchen 0c19482d03 chore(.gitignore): ignore IntelliJ IDEA module files
Ignore IntelliJ IDEA modules files in the git repository

Closes #5273
2014-11-04 12:46:31 -05:00
Tero Parviainen 0f7bcfdf93 docs(guide/scope): describe watch depths
Describe and visualize the three watch strategies: By reference, by collection items, and by value.

Closes #9388
2014-11-03 12:44:16 -08:00
Loring Dodge 9a26ab5870 docs($route): replace comma with "and"
Remove a comma and replace with "and" on line 444

Closes #9889
2014-11-03 20:58:46 +01:00
jbnizet d906ed3100 docs(input): replace dateTimeLocal by datetime-local
Fixes #9856
Closes #9870
2014-11-02 19:58:32 +01:00
Martin Staffa 0b16d10d2d docs(ngValue): clarify the limitations of ngValue and option elements 2014-11-02 19:47:09 +01:00
Martin Staffa e0198c1cda docs(ngModelController): add $name property 2014-11-02 18:43:52 +01:00
Martin Staffa ba731ab850 docs(ngModelController): remove bogus @param fields 2014-11-02 18:43:52 +01:00
Caitlin Potter e69c1806a8 style(routeSpec.js): make jshint happy 2014-11-01 18:11:51 -04:00
cmichal b4770582f8 fix(ngRoute): allow proto inherited properties in route params object
copy route params with angular.copy before using angular.extend which looks only for enumerable own
properties

Closes #8181
Closes #9731
2014-11-01 18:08:24 -04:00
Henry Zhu 2a2fd14c08 docs(guide/Forms): clarify ngModel behavior for validation
Bueno!

Closes #9866
2014-10-31 18:08:22 -04:00
Jeff Cross e4eb382f2d docs(changelog): add release notes for 1.3.1 spectral-lobster 2014-10-31 10:39:14 -07:00
388 changed files with 10440 additions and 5542 deletions
+1
View File
@@ -13,6 +13,7 @@ angular.js.tmproj
bower_components/
angular.xcodeproj
.idea
*.iml
.agignore
.lvimrc
libpeerconnection.log
+8
View File
@@ -23,8 +23,16 @@
"disallowTrailingWhitespace": true,
"requireCommaBeforeLineBreak": true,
"requireLineFeedAtFileEnd": true,
"requireSpaceAfterBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"],
"requireSpaceBeforeBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"],
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
"requireSpaceBeforeBlockStatements": true,
"requireSpacesInConditionalExpression": {
"afterTest": true,
"beforeConsequent": true,
"afterConsequent": true,
"beforeAlternate": true
},
"requireSpacesInFunction": {
"beforeOpeningCurlyBrace": true
},
-2
View File
@@ -5,8 +5,6 @@
{
"requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"],
"requireSpaceAfterBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"],
"requireSpaceBeforeBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"],
"disallowImplicitTypeConversion": ["string"],
"disallowMultipleLineBreaks": true,
"disallowKeywordsOnNewLine": ["else"],
+293
View File
@@ -1,3 +1,294 @@
<a name="1.3.4"></a>
# 1.3.4 highfalutin-petroglyph (2014-11-24)
## Bug Fixes
- **$browser:** allow chaining url() calls in setter mode
([764fa869](https://github.com/angular/angular.js/commit/764fa869dd8809d494924c23f30ddaa4cac84249),
[#10157](https://github.com/angular/angular.js/issues/10157))
- **$http:** return empty headers, ignore properties in Object prototype
([637c020f](https://github.com/angular/angular.js/commit/637c020f828a7ceeaacf83bb1a54ed3092e6c273),
[#7779](https://github.com/angular/angular.js/issues/7779), [#10113](https://github.com/angular/angular.js/issues/10113), [#10091](https://github.com/angular/angular.js/issues/10091))
- **$locale:** Allow currency filter to fall back to maxFrac from locale
([6dbd606a](https://github.com/angular/angular.js/commit/6dbd606ad7b708d5886c0e7ffee20ae8f8719711),
[#10179](https://github.com/angular/angular.js/issues/10179))
- **$location:** allow empty string URLs to reset path, search, and hash
([7812dfce](https://github.com/angular/angular.js/commit/7812dfcee8ab98cbf38261f9948d9541656bf554),
[#10063](https://github.com/angular/angular.js/issues/10063), [#10064](https://github.com/angular/angular.js/issues/10064))
- **$route:** fix redirection with optional/eager params
([891acf4c](https://github.com/angular/angular.js/commit/891acf4c201823fd2c925ee321c70d06737d5944),
[#9819](https://github.com/angular/angular.js/issues/9819), [#9827](https://github.com/angular/angular.js/issues/9827))
- **Angular:** properly get node name for svg element wrapper
([09a98323](https://github.com/angular/angular.js/commit/09a9832358960c98392c9df1a9fd9592f59bc844),
[#10078](https://github.com/angular/angular.js/issues/10078), [#10172](https://github.com/angular/angular.js/issues/10172))
- **NgModelController:** typo $rawModelValue -> $$rawModelValue
([4f4ff5f3](https://github.com/angular/angular.js/commit/4f4ff5f31b82c6f7be409ea4edbad4c2913ac1f1))
- **input:**
- set ngTrueValue on required checkbox
([8692f87a](https://github.com/angular/angular.js/commit/8692f87a4689fa0dd3640f4dcab5c6b6f960489b),
[#5164](https://github.com/angular/angular.js/issues/5164))
- call $setTouched in blur asynchronously if necessary
([eab27187](https://github.com/angular/angular.js/commit/eab271876cb87c1f5f6c6f29e814fb8fecad87ff),
[#8762](https://github.com/angular/angular.js/issues/8762), [#9808](https://github.com/angular/angular.js/issues/9808), [#10014](https://github.com/angular/angular.js/issues/10014))
- **input[date]:** do not use `$isEmpty` to check the model validity
([40406e2f](https://github.com/angular/angular.js/commit/40406e2f22713efbd37ef3eff408339727cb62d9))
- **linky:** encode double quotes when serializing email addresses
([8ee8ffeb](https://github.com/angular/angular.js/commit/8ee8ffeba0a5a133fa792745c1019d294ecfcef3),
[#8945](https://github.com/angular/angular.js/issues/8945), [#8964](https://github.com/angular/angular.js/issues/8964), [#5946](https://github.com/angular/angular.js/issues/5946), [#10090](https://github.com/angular/angular.js/issues/10090), [#9256](https://github.com/angular/angular.js/issues/9256))
- **ngMaxlength:** ignore maxlength when not set to a non-negative integer
([92f87b11](https://github.com/angular/angular.js/commit/92f87b114242b01876e1dc5c6fddd061352ecb2c),
[#9874](https://github.com/angular/angular.js/issues/9874))
- **ngModel:** don't run parsers when executing $validate
([e3764e30](https://github.com/angular/angular.js/commit/e3764e30a301ec6136c8e6b5493d39feb3cd1ecc))
- **ngModelOptions:** preserve context of getter/setters
([bb4d3b73](https://github.com/angular/angular.js/commit/bb4d3b73a1ccf3dee55b0c25baf031bae5cbb676),
[#9394](https://github.com/angular/angular.js/issues/9394), [#9865](https://github.com/angular/angular.js/issues/9865))
## Features
- **ngMaxlength:** add support for disabling max length limit
([5c1fdff6](https://github.com/angular/angular.js/commit/5c1fdff691b9367d73f72f6a0298cb6a6e259f35),
[#9995](https://github.com/angular/angular.js/issues/9995))
- **ngModelController:** add $setDirty method
([e8941c0f](https://github.com/angular/angular.js/commit/e8941c0fe5217d2e705bad8253dc0162aff4c709),
[#10038](https://github.com/angular/angular.js/issues/10038), [#10049](https://github.com/angular/angular.js/issues/10049))
- **ngPluralize:** add support for `count` to be a one-time expression
([2b41a586](https://github.com/angular/angular.js/commit/2b41a5868aee79e3872ad92db66e30959207d98e),
[#10004](https://github.com/angular/angular.js/issues/10004))
## Performance Improvements
- use Object.create instead of creating temporary constructors
([bf6a79c3](https://github.com/angular/angular.js/commit/bf6a79c3484f474c300b5442ae73483030ef5782),
[#10058](https://github.com/angular/angular.js/issues/10058))
## Breaking Changes
- **ngModelOptions:** due to [bb4d3b73](https://github.com/angular/angular.js/commit/bb4d3b73a1ccf3dee55b0c25baf031bae5cbb676),
previously, ngModel invoked getter/setters in the global context.
For example:
```js
<input ng-model="model.value" ng-model-options="{ getterSetter: true }">
```
would previously invoke `model.value()` in the global context.
Now, ngModel invokes `value` with `model` as the context.
It's unlikely that real apps relied on this behavior. If they did they can use `.bind` to explicilty
bind a getter/getter to the global context, or just reference globals normally without `this`.
<a name="1.2.27"></a>
# 1.2.27 prime-factorization (2014-11-20)
## Bug Fixes
- **$animate:** clear the GCS cache even when no animation is detected
([f619d032](https://github.com/angular/angular.js/commit/f619d032c932752313c646b5295bad8a68ef3871),
[#8813](https://github.com/angular/angular.js/issues/8813))
- **$browser:**
- Cache `location.href` only during page reload phase
([434d7a09](https://github.com/angular/angular.js/commit/434d7a09039151c1e627ac156213905d06b7df10),
[#9235](https://github.com/angular/angular.js/issues/9235), [#9470](https://github.com/angular/angular.js/issues/9470))
- dont use history api when only the hash changes
([a6e6438d](https://github.com/angular/angular.js/commit/a6e6438dae1ed92b29608d0b8830b0a7fbb624ef),
[#9423](https://github.com/angular/angular.js/issues/9423), [#9424](https://github.com/angular/angular.js/issues/9424))
- handle async href on url change in <=IE9
([fe7d9ded](https://github.com/angular/angular.js/commit/fe7d9dedaa5ec3b3f56d9eb9c513cf99e40121ce),
[#9235](https://github.com/angular/angular.js/issues/9235))
- **$http:** add missing shortcut methods and missing docs
([ec4fe1bc](https://github.com/angular/angular.js/commit/ec4fe1bcab6f981103a10f860a3a00122aa78607),
[#9180](https://github.com/angular/angular.js/issues/9180), [#9321](https://github.com/angular/angular.js/issues/9321))
- **$location:**
- revert erroneous logic and backport refactorings from master
([1ee9b4ef](https://github.com/angular/angular.js/commit/1ee9b4ef5e4a795061d3aa19adefdeb7e0209eeb),
[#8492](https://github.com/angular/angular.js/issues/8492))
- allow 0 in path() and hash()
([f807d7ab](https://github.com/angular/angular.js/commit/f807d7ab4ebd18899154528ea9ed50d5bc25c57a))
- **$parse:** add quick check for Function constructor in fast path
([756640f5](https://github.com/angular/angular.js/commit/756640f5aa8f3fd0084bff50534e23976a6fff00))
- **$parse, events:** prevent accidental misuse of properties on $event
([4d0614fd](https://github.com/angular/angular.js/commit/4d0614fd0da12c5783dfb4956c330edac87e62fe),
[#9969](https://github.com/angular/angular.js/issues/9969))
- **ngMock:** $httpBackend should match data containing Date objects correctly
([1426b029](https://github.com/angular/angular.js/commit/1426b02980badfd322eb960d71bfb1a14d657847),
[#5127](https://github.com/angular/angular.js/issues/5127))
- **orderBy:** sort by identity if no predicate is given
([45b896a1](https://github.com/angular/angular.js/commit/45b896a16abbcbfcdfb9a95c2d10c76a805b57cc),
[#5847](https://github.com/angular/angular.js/issues/5847), [#4579](https://github.com/angular/angular.js/issues/4579), [#9403](https://github.com/angular/angular.js/issues/9403))
- **select:** ensure the label attribute is updated in Internet Explorer
([16833d0f](https://github.com/angular/angular.js/commit/16833d0fb6585117e9978d1accc3ade83e22e797),
[#9621](https://github.com/angular/angular.js/issues/9621), [#10042](https://github.com/angular/angular.js/issues/10042))
## Performance Improvements
- **orderBy:** copy array with slice instead of for loop
([409bcb38](https://github.com/angular/angular.js/commit/409bcb3810a1622178268f7ff7f4130887a1a3dc),
[#9942](https://github.com/angular/angular.js/issues/9942))
<a name="1.3.3"></a>
# 1.3.3 undersea-arithmetic (2014-11-17)
## Bug Fixes
- **$http:** don't parse single space responses as JSON
([6f19a6fd](https://github.com/angular/angular.js/commit/6f19a6fd33ab72d3908e3418fba47ee8e1598fa6),
[#9907](https://github.com/angular/angular.js/issues/9907))
- **minErr:** stringify non-JSON compatible objects in error messages
([cf43ccdf](https://github.com/angular/angular.js/commit/cf43ccdf9b8665a2fd5d6aa52f80cb2d7c9bb7e2),
[#10085](https://github.com/angular/angular.js/issues/10085))
- **$rootScope:** handle cyclic references in scopes when creating error messages
([e80053d9](https://github.com/angular/angular.js/commit/e80053d91fd7c722e092a23d326384de2e552eb6),
[#10085](https://github.com/angular/angular.js/issues/10085))
- **ngRepeat:** support cyclic object references in error messages
([fa12c3c8](https://github.com/angular/angular.js/commit/fa12c3c86af7965d1b9d9a5dd3434755e9e04635),
[#9838](https://github.com/angular/angular.js/issues/9838), [#10065](https://github.com/angular/angular.js/issues/10065), [#10085](https://github.com/angular/angular.js/issues/10085))
- **ngMock:** call $interval callbacks even when invokeApply is false
([d81ff888](https://github.com/angular/angular.js/commit/d81ff8885b77f70c6417d7be3124d86d07447375),
[#10032](https://github.com/angular/angular.js/issues/10032))
- **ngPattern:** match behaviour of native HTML pattern attribute
([85eb9660](https://github.com/angular/angular.js/commit/85eb9660ef67c24d5104a6a1921bedad0bd1b57e),
[#9881](https://github.com/angular/angular.js/issues/9881), [#9888](https://github.com/angular/angular.js/issues/9888))
- **select:** ensure the label attribute is updated in Internet Explorer
([6604c236](https://github.com/angular/angular.js/commit/6604c2361427fba8c43a39dc2e92197390dfbdbe),
[#9621](https://github.com/angular/angular.js/issues/9621), [#10042](https://github.com/angular/angular.js/issues/10042))
## Features
- **$location:** allow to location to be changed during $locationChangeStart
([a9352c19](https://github.com/angular/angular.js/commit/a9352c19ce33f0393d6581547c7ea8dfc2a8b78f),
[#9607](https://github.com/angular/angular.js/issues/9607), [#9678](https://github.com/angular/angular.js/issues/9678))
- **$routeProvider:** allow setting caseInsensitiveMatch on the provider
([0db573b7](https://github.com/angular/angular.js/commit/0db573b7493f76abd94ff65ce660017d617e865b),
[#6477](https://github.com/angular/angular.js/issues/6477), [#9873](https://github.com/angular/angular.js/issues/9873))
## Performance Improvements
- **orderBy:** copy array with slice instead of for loop
([8eabc546](https://github.com/angular/angular.js/commit/8eabc5463c795d87f37e5a9eacbbb14435024061),
[#9942](https://github.com/angular/angular.js/issues/9942))
## Breaking Changes
- **$parse:** due to [fbad2805](https://github.com/angular/angular.js/commit/fbad2805703569058a4a860747b0e2d8aee36bdf),
you can't use characters that have special meaning in AngularJS expressions (ex.: `.` or `-`)
as part of filter's name. Before this commit custom filters could contain special characters
(like a dot) in their name but this wasn't intentional.
<a name="1.3.2"></a>
# 1.3.2 cardiovasculatory-magnification (2014-11-07)
## Bug Fixes
- **$compile:** do not rebind parent bound transclude functions
([841c0907](https://github.com/angular/angular.js/commit/841c0907556f525dbc4223609d808319fe0dd7e2),
[#9413](https://github.com/angular/angular.js/issues/9413))
- **$parse:**
- stateful interceptors override an `undefined` expression
([ed99821e](https://github.com/angular/angular.js/commit/ed99821e4dc621864f7e2d9a6b5305fca27fb7fa),
[#9821](https://github.com/angular/angular.js/issues/9821), [#9825](https://github.com/angular/angular.js/issues/9825))
- add quick check for Function constructor in fast path
([e676d642](https://github.com/angular/angular.js/commit/e676d642f5feb8d3ba88944634afb479ba525c36))
- **$parse, events:** prevent accidental misuse of properties on $event
([e057a9aa](https://github.com/angular/angular.js/commit/e057a9aa398ead209bd6bbf76e22d2d5562904fb))
- **ngRoute:** allow proto inherited properties in route params object
([b4770582](https://github.com/angular/angular.js/commit/b4770582f84f26c8ff7f2320a36a6b0ceff6e6cc),
[#8181](https://github.com/angular/angular.js/issues/8181), [#9731](https://github.com/angular/angular.js/issues/9731))
- **select:** use strict comparison for isSelected with selectAs
([9e305948](https://github.com/angular/angular.js/commit/9e305948e4965fb86b0c79985dc6e8c59a9c66af),
[#9639](https://github.com/angular/angular.js/issues/9639), [#9949](https://github.com/angular/angular.js/issues/9949))
## Features
- **ngAria:** announce ngMessages with aria-live
([187e4318](https://github.com/angular/angular.js/commit/187e43185dfb1bce6a318d95958c73cfb789d33c),
[#9834](https://github.com/angular/angular.js/issues/9834))
- **ngMock:** decorator that adds Scope#$countChildScopes and Scope#$countWatchers
([74981c9f](https://github.com/angular/angular.js/commit/74981c9f208b3617cbf00beafd61138d25c5d546),
[#9926](https://github.com/angular/angular.js/issues/9926), [#9871](https://github.com/angular/angular.js/issues/9871))
## Security Note
This release also contains security fixes for expression sandbox bypasses.
These issues affect only applications with known server-side XSS holes that are also using [CSP](https://developer.mozilla.org/en-US/docs/Web/Security/CSP) to secure their client-side code. If your application falls into this rare category, we recommend updating your version of Angular.
We'd like to thank security researches [Sebastian Lekies](https://twitter.com/sebastianlekies), [Jann Horn](http://thejh.net/), and [Gábor Molnár](https://twitter.com/molnar_g) for reporting these issues to us.
We also added a documentation page focused on security, which contains some of the best practices, DOs and DON'Ts. Please check out [https://docs.angularjs.org/guide/security](https://docs.angularjs.org/guide/security).
<a name="1.3.1"></a>
# 1.3.1 spectral-lobster (2014-10-31)
## Bug Fixes
- **$compile:** returning null when an optional controller is not found
([2cd5b4ec](https://github.com/angular/angular.js/commit/2cd5b4ec4409a818ccd33a6fbdeb99a3443a1809),
[#9404](https://github.com/angular/angular.js/issues/9404), [#9392](https://github.com/angular/angular.js/issues/9392))
- **$observe:** check if the attribute is undefined
([531a8de7](https://github.com/angular/angular.js/commit/531a8de72c439d8ddd064874bf364c00cedabb11),
[#9707](https://github.com/angular/angular.js/issues/9707), [#9720](https://github.com/angular/angular.js/issues/9720))
- **$parse:** support dirty-checking objects with null prototype
([28661d1a](https://github.com/angular/angular.js/commit/28661d1a8cc3a8454bad7ae531e027b1256476c9),
[#9568](https://github.com/angular/angular.js/issues/9568))
- **$sce:** use msie instead of $document[0].documentMode
([45252c3a](https://github.com/angular/angular.js/commit/45252c3a545336a0bac93be6ee28cde6afaa3cb4),
[#9661](https://github.com/angular/angular.js/issues/9661))
- **$templateRequest:** ignore JSON Content-Type header and content
([1bd473eb](https://github.com/angular/angular.js/commit/1bd473eb4587900086e0b6b308dcf1dcfe9760d9),
[#5756](https://github.com/angular/angular.js/issues/5756), [#9619](https://github.com/angular/angular.js/issues/9619))
- **i18n:** rename datetimeSymbols to be camelCase
([94f5a285](https://github.com/angular/angular.js/commit/94f5a285bfcf04d800afc462a7a37a3469d77f1a))
- **loader:** fix double spaces
([8b2f1a47](https://github.com/angular/angular.js/commit/8b2f1a47b584ceb98689f48538a2af73cd65dfd8),
[#9630](https://github.com/angular/angular.js/issues/9630))
- **ngMock:** $httpBackend should match data containing Date objects correctly
([1025f6eb](https://github.com/angular/angular.js/commit/1025f6ebf4e5933a12920889be00cd8ac8a106fa),
[#5127](https://github.com/angular/angular.js/issues/5127))
- **ngSanitize:** attribute name: xmlns:href -> xlink:href
([4cccf0f2](https://github.com/angular/angular.js/commit/4cccf0f2a89b002d63cb443e1e7b15f76dcef425),
[#9769](https://github.com/angular/angular.js/issues/9769))
- **select:** assign result of track exp to element value
([4b4098bf](https://github.com/angular/angular.js/commit/4b4098bfcae64f69c70a22393de1f3d9a0d3dc46),
[#9718](https://github.com/angular/angular.js/issues/9718), [#9592](https://github.com/angular/angular.js/issues/9592))
- **templateRequest:** allow empty html template
([52ceec22](https://github.com/angular/angular.js/commit/52ceec2229dc132b76da4e022c91474344f2d906),
[#9581](https://github.com/angular/angular.js/issues/9581))
- **testability:** escape regex chars in `findBindings` if using `exactMatch`
([02aa4f4b](https://github.com/angular/angular.js/commit/02aa4f4b85ee15922a1f2de8ba78f562c18518d0),
[#9595](https://github.com/angular/angular.js/issues/9595), [#9600](https://github.com/angular/angular.js/issues/9600))
## Features
- **$compile:** allow $watchCollection to be used in bi-directional bindings
([40bbc981](https://github.com/angular/angular.js/commit/40bbc9817845bf75581daee5d0ec30980affb0f5),
[#9725](https://github.com/angular/angular.js/issues/9725))
- **ngSanitize:** accept SVG elements and attributes
([a54b25d7](https://github.com/angular/angular.js/commit/a54b25d77999a85701dfc5396fef78e586a99667),
[#9578](https://github.com/angular/angular.js/issues/9578), [#9751](https://github.com/angular/angular.js/issues/9751))
<a name="1.3.0"></a>
# 1.3.0 superluminal-nudge (2014-10-13)
@@ -1452,6 +1743,8 @@ Closes #8230
- **jQuery:** due to [9e7cb3c3](https://github.com/angular/angular.js/commit/9e7cb3c37543008e6236bb5a2c4536df2e1e43a9),
Angular no longer supports jQuery versions below 2.1.1.
- **$q:** due to [23bc92b1](https://github.com/angular/angular.js/commit/23bc92b17df882a907fb326320f0622717fefe7b),
Promises methods are no longer enumerated when using for-loops with `hasOwnProperty` check. E.g. `angular.extends`
<a name="1.2.22"></a>
+2
View File
@@ -5,6 +5,7 @@ var angularFiles = {
'src/minErr.js',
'src/Angular.js',
'src/loader.js',
'src/stringify.js',
'src/AngularPublic.js',
'src/jqLite.js',
'src/apis.js',
@@ -73,6 +74,7 @@ var angularFiles = {
],
'angularLoader': [
'stringify.js',
'src/minErr.js',
'src/loader.js'
],
+2 -1
View File
@@ -71,7 +71,8 @@ app.controller('DataController', function($scope, $rootScope) {
date2: new Date(Math.random()*Date.now()),
func: function(){ return star; },
obj: data[i-1],
keys: data[i-1] && (data[i-1].keys || Object.keys(data[i-1]))
keys: data[i-1] && (data[i-1].keys || Object.keys(data[i-1])),
constructor: data[i-1]
});
}
@@ -16,6 +16,12 @@
<label for="complexPath">Complex Paths</label>
</li>
<li>
<input type="radio" ng-model="expressionType" value="constructorPath" id="constructorPath">
<label for="constructorPath">Constructor Paths</label>
($parse special cases "constructor" for security)
</li>
<li>
<input type="radio" ng-model="expressionType" value="fieldAccess" id="fieldAccess">
<label for="fieldAccess">Field Accessors</label>
@@ -77,6 +83,17 @@
<span bm-pe-watch="row.keys"></span>
</li>
<li ng-switch-when="constructorPath" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="row.index"></span>
<span bm-pe-watch="row.constructor.index"></span>
<span bm-pe-watch="row.constructor.index"></span>
<span bm-pe-watch="row.constructor.index"></span>
<span bm-pe-watch="row.constructor.constructor.index"></span>
<span bm-pe-watch="row.constructor.constructor.index"></span>
<span bm-pe-watch="row.constructor.constructor.constructor.index"></span>
<span bm-pe-watch="row.constructor.constructor.constructor.index"></span>
</li>
<li ng-switch-when="complexPath" ng-repeat="(rowIdx, row) in ::data">
<span bm-pe-watch="row.index"></span>
<span bm-pe-watch="row.num0"></span>
+4
View File
@@ -128,6 +128,10 @@ h1,h2,h3,h4,h5,h6 {
margin-bottom:5px;
}
.nav-index-group .nav-index-listing.current a {
color: #B52E31;
}
.nav-breadcrumb {
margin:4px 0;
padding:0;
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

+1
View File
@@ -13,6 +13,7 @@ angular.module('DocsController', [])
$scope.navClass = function(navItem) {
return {
active: navItem.href && this.currentPage && this.currentPage.path,
current: this.currentPage && this.currentPage.path === navItem.href,
'nav-index-section': navItem.type === 'section'
};
};
+4 -2
View File
@@ -1,7 +1,10 @@
"use strict";
angular.module('versions', [])
.controller('DocsVersionsCtrl', ['$scope', '$location', '$window', 'NG_VERSIONS', function($scope, $location, $window, NG_VERSIONS) {
$scope.docs_version = NG_VERSIONS[0];
$scope.docs_versions = NG_VERSIONS;
for(var i=0, minor = NaN; i < NG_VERSIONS.length; i++) {
var version = NG_VERSIONS[i];
@@ -13,9 +16,8 @@ angular.module('versions', [])
minor = version.minor;
}
$scope.docs_versions = NG_VERSIONS;
$scope.getGroupName = function(v) {
return v.isLatest ? 'Latest' : (v.isStable ? 'Stable' : 'Unstable');
return v.isLatest ? 'Latest' : ('v' + v.major + '.' + v.minor + '.x');
};
$scope.jumpToDocsVersion = function(version) {
+21
View File
@@ -161,6 +161,27 @@ or JavaScript callbacks.
</tr>
</table>
## {@link ngAria ngAria}
Use ngAria to inject common accessibility attributes into directives and improve the experience for users with disabilities.
<div class="alert alert-info">Include the **angular-aria.js** file and set ngAria as a dependency for this to work in your application.</div>
<table class="definition-table spaced">
<tr>
<td>
{@link ngAria#service Services}
</td>
<td>
<p>
The {@link ngAria.$aria $aria} service contains helper methods for applying ARIA attributes to HTML.
<p>
<p>
{@link ngAria.$ariaProvider $ariaProvider} is used for configuring ARIA attributes.
</p>
</td>
</tr>
</table>
## {@link ngResource ngResource}
+10
View File
@@ -0,0 +1,10 @@
@ngdoc error
@name $http:badreq
@fullName Bad Request Configuration
@description
This error occurs when the request configuration parameter passed to the {@link ng.$http `$http`} service is not an object.  `$http` expects a single parameter, the request configuration object, but received a parameter that was not an object.  The error message should provide additional context such as the actual value of the parameter that was received.  If you passed a string parameter, perhaps you meant to call one of the shorthand methods on `$http` such as `$http.get(…)`, etc.
To resolve this error, make sure you pass a valid request configuration object to `$http`.
For more information, see the {@link ng.$http `$http`} service API documentation.
+1 -1
View File
@@ -61,7 +61,7 @@ Attempting to inject one controller into another will also throw an `Unknown pro
```
angular.module('myModule', [])
.controller('MyFirstController', function() { /* ... */ });
.controller('MyFirstController', function() { /* ... */ })
.controller('MySecondController', ['MyFirstController', function(MyFirstController) {
// This controller throws an unknown provider error because
// MyFirstController cannot be injected.
+3 -3
View File
@@ -8,10 +8,10 @@ This error occurs when {@link ng.$location $location} service is configured to u
For example if you configure `$location` service with prefix `'!'`:
```
myApp.config(function($locationProvider) {
$locationProvider.prefix('!');
$locationProvider.hashPrefix('!');
});
```
If you enter the app at url `http:/myapp.com/#/myView` this error will be throw.
If you enter the app at url `http://myapp.com/#/myView` this error will be thrown.
The correct url for this configuration is `http:/myapp.com/#!/myView` (note the `'!'` after `'#'` symbol).
The correct url for this configuration is `http://myapp.com/#!/myView` (note the `'!'` after `'#'` symbol).
+1 -1
View File
@@ -344,7 +344,7 @@ to anchors on the same page without needing to know on which page the user curre
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links
to entry point of your application (e.g. index.html). Requiring a `<base>` tag is also important for
this case, as it allows Angular to differentiate between the part of the url that is the application
base and the path that should be handeled by the application.
base and the path that should be handled by the application.
### Sending links among different browsers
+335 -31
View File
@@ -6,56 +6,360 @@
# Accessibility with ngAria
You can use the `ngAria` module to have common ARIA attributes automatically applied when you
use certain directives. To enable `ngAria`, just require the module into your application and
the code will hook into your ng-show/ng-hide, input, textarea, button, select and
ng-required directives and add the appropriate ARIA states and properties.
The goal of ngAria is to improve Angular's default accessibility by enabling common
[ARIA](http://www.w3.org/TR/wai-aria/) attributes that convey state or semantic information for
assistive technologies used by persons with disabilities.
Currently, the following attributes are implemented:
* aria-hidden
* aria-checked
* aria-disabled
* aria-required
* aria-invalid
* aria-multiline
* aria-valuenow
* aria-valuemin
* aria-valuemax
* tabindex
##Including ngAria
You can disable individual attributes by using the `{@link ngAria.$ariaProvider#config config}` method.
###Example
Using {@link ngAria ngAria} is as simple as requiring the ngAria module in your application. ngAria hooks into
standard AngularJS directives and quietly injects accessibility support into your application
at runtime.
```js
angular.module('myApp', ['ngAria'])...
```
Elements using `ng-model` with `required` or `ngRequired` directives will automatically have
`aria-required` attributes with the proper corresponding values.
###Using ngAria
Most of what ngAria does is only visible "under the hood". To see the module in action, once you've
added it as a dependency, you can test a few things:
* Using your favorite element inspector, look for ngAria attributes in your own code.
* Test using your keyboard to ensure `tabindex` is used correctly.
* Fire up a screen reader such as VoiceOver to listen for ARIA support.
[Helpful screen reader tips.](http://webaim.org/articles/screenreader_testing/)
##Supported directives
Currently, ngAria interfaces with the following directives:
* <a href="#ngmodel">ngModel</a>
* <a href="#ngdisabled">ngDisabled</a>
* <a href="#ngshow">ngShow</a>
* <a href="#nghide">ngHide</a>
* <a href="#ngclick-and-ngdblclick">ngClick</a>
* <a href="#ngclick-and-ngdblclick">ngDblClick</a>
<h2 id="ngmodel">ngModel</h2>
Most of ngAria's heavy lifting happens in the {@link ngModel ngModel}
directive. For elements using ngModel, special attention is paid by ngAria if that element also
has a a role or type of `checkbox`, `radio`, `range` or `textbox`.
For those elements using ngModel, ngAria will dynamically bind and update the following ARIA
attributes (if they have not been explicitly specified by the developer):
* aria-checked
* aria-valuemin
* aria-valuemax
* aria-valuenow
* aria-invalid
* aria-required
###Example
<example module="ngAria_ngModelExample" deps="angular-aria.js">
<file name="index.html">
<style>
[role=checkbox] {
cursor: pointer;
display: inline-block;
}
[role=checkbox] .icon:before {
content: '\2610';
display: inline-block;
font-size: 2em;
line-height: 1;
vertical-align: middle;
speak: none;
}
[role=checkbox].active .icon:before {
content: '\2611';
}
pre {
white-space: pre-wrap;
}
</style>
<div>
<form ng-controller="formsController">
<some-checkbox role="checkbox" ng-model="checked" ng-class="{active: checked}"
ng-disabled="isDisabled" ng-click="toggleCheckbox()"
aria-label="Custom Checkbox" show-attrs>
<span class="icon" aria-hidden="true"></span>
Custom Checkbox
</some-checkbox>
</form>
</div>
<script>
var app = angular.module('ngAria_ngModelExample', ['ngAria'])
.controller('formsController', function($scope){
$scope.checked = false;
$scope.toggleCheckbox = function(){
$scope.checked = !$scope.checked;
}
})
.directive('someCheckbox', function(){
return {
restrict: 'E',
link: function($scope, $el, $attrs) {
$el.on('keypress', function(event){
event.preventDefault();
if(event.keyCode === 32 || event.keyCode === 13){
$scope.toggleCheckbox();
$scope.$apply();
}
});
}
}
})
.directive('showAttrs', function() {
return function($scope, $el, $attrs) {
var pre = document.createElement('pre');
$el.after(pre);
$scope.$watch(function() {
var $attrs = {};
Array.prototype.slice.call($el[0].attributes, 0).forEach(function(item) {
if (item.name !== 'show-$attrs') {
$attrs[item.name] = item.value;
}
});
return $attrs;
}, function(newAttrs, oldAttrs) {
pre.textContent = JSON.stringify(newAttrs, null, 2);
}, true);
}
});
</script>
</file>
</example>
ngAria will also add `tabIndex`, ensuring custom elements with these roles will be reachable from
the keyboard. It is still up to **you** as a developer to **ensure custom controls will be
operable** from the keybard. Think of `ng-click` on a `<div>` or `<md-checkbox>`: you still need
to bind `ng-keypress` to make it fully operable from the keyboard. As a rule, any time you create
a widget involving user interaction, be sure to test it with your keyboard and at least one mobile
and desktop screen reader (preferably more).
<h2 id="ngdisabled">ngDisabled</h2>
The `disabled` attribute is only valid for certain elements such as `button`, `input` and
`textarea`. To properly disable custom element directives such as `<md-checkbox>` or `<taco-tab>`,
using ngAria with [ngDisabled](https://docs.angularjs.org/api/ng/directive/ngDisabled) will also
add `aria-disabled`. This tells assistive technologies when a non-native input is disabled, helping
custom controls to be more accessible.
###Example
```html
<material-input ng-model="val" required>
<md-checkbox ng-disabled="disabled">
```
Becomes:
```html
<material-input ng-model="val" required aria-required="true">
<md-checkbox disabled aria-disabled="true">
```
ngAria is just a starting point. You'll have to manually choose how to implement some
accessibility features.
>You can check whether a control is legitimately disabled for a screen reader by visiting
[chrome://accessibility](chrome://accessibility).
For instance, you may want to add `ng-keypress` bindings alongside `ng-click` to make keyboard
navigation easier.
<h2 id="ngshow">ngShow</h2>
>The [ngShow](https://docs.angularjs.org/api/ng/directive/ngShow) directive shows or hides the
given HTML element based on the expression provided to the `ngShow` attribute. The element is
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
## Additional Resources
In its default setup, ngAria for `ngShow` is actually redundant. It toggles `aria-hidden` on the
directive when it is hidden or shown. However, the default CSS of `display: none !important`,
already hides child elements from a screen reader. It becomes more useful when the default
CSS is overridden with properties that dont affect assistive technologies, such as `opacity`
or `transform`. By toggling `aria-hidden` dynamically with ngAria, we can ensure content visually
hidden with this technique will not be read aloud in a screen reader.
One caveat with this combination of CSS and `aria-hidden`: you must also remove links and other
interactive child elements from the tab order using `tabIndex=“-1”` on each control. This ensures
screen reader users won't accidentally focus on "mystery elements". Managing tab index on every
child control can be complex and affect performance, so its best to just stick with the default
`display: none` CSS. See the [fourth rule of ARIA use](http://www.w3.org/TR/aria-in-html/#fourth-rule-of-aria-use).
###Example
```css
.ng-hide {
display: block;
opacity: 0;
}
```
```html
<div ng-show="false" class="ng-hide" aria-hidden="true"></div>
```
Becomes:
```html
<div ng-show="true" aria-hidden="false"></div>
```
*Note: Child links, buttons or other interactive controls must also be removed from the tab order.*
<h2 id="nghide">ngHide</h2>
>The [ngHide](https://docs.angularjs.org/api/ng/directive/ngHide) directive shows or hides the
given HTML element based on the expression provided to the `ngHide` attribute. The element is
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
The default CSS for `ngHide`, the inverse method to `ngShow`, makes ngAria redundant. It toggles
`aria-hidden` on the directive when it is hidden or shown, but the content is already hidden with
`display: none`. See explanation for <a href="#ngshow">ngShow</a> when overriding the default CSS.
<h2 id="ngclick-and-ngdblclick">ngClick and ngDblclick</h2>
If `ng-click` or `ng-dblclick` is encountered, ngAria will add `tabindex` if it isn't there already.
Even with this, you must currently still add `ng-keypress` to non-interactive elements such as `div`
or `taco-button` to enable keyboard access. Conversation is currently ongoing about whether ngAria
should also bind `ng-keypress`.
<h3>Example</h3>
```html
<div ng-click="toggleMenu()"></div>
```
Becomes:
```html
<div ng-click="toggleMenu()" tabindex="0"></div>
```
*Note: ngAria still requires `ng-keypress` to be added manually to non-native controls like divs.*
<h2 id="ngmessages">ngMessages</h2>
The new ngMessages module makes it easy to display form validation or other messages with priority
sequencing and animation. To expose these visual messages to screen readers,
ngAria injects `aria-live="polite"`, causing them to be read aloud any time a message is shown,
regardless of the user's focus location.
###Example
```html
<div ng-messages="myForm.myName.$error">
<div ng-message="required">You did not enter a field</div>
<div ng-message="maxlength">Your field is too long</div>
</div>
```
Becomes:
```html
<div ng-messages="myForm.myName.$error" aria-live="polite">
<div ng-message="required">You did not enter a field</div>
<div ng-message="maxlength">Your field is too long</div>
</div>
```
##Disabling attributes
The attribute magic of ngAria may not work for every scenario. To disable individual attributes,
you can use the {@link ngAria.$ariaProvider#config config} method. Just keep in mind this will
tell ngAria to ignore the attribute globally.
<example module="ngAria_ngDisabledExample" deps="angular-aria.js">
<file name="index.html">
<style>
[role=checkbox] {
cursor: pointer;
display: inline-block;
}
[role=checkbox] .icon:before {
content: '\2610';
display: inline-block;
font-size: 2em;
line-height: 1;
vertical-align: middle;
speak: none;
}
[role=checkbox].active .icon:before {
content: '\2611';
}
</style>
<form ng-controller="formsController">
<div ng-model="someModel" show-attrs>
Div with ngModel and aria-invalid disabled
</div>
<div role="checkbox" ng-model="checked" ng-class="{active: checked}"
aria-label="Custom Checkbox" ng-click="toggleCheckbox()" some-checkbox show-attrs>
<span class="icon" aria-hidden="true"></span>
Custom Checkbox for comparison
</div>
</form>
<script>
angular.module('ngAria_ngDisabledExample', ['ngAria'], function config($ariaProvider) {
$ariaProvider.config({
ariaInvalid: false,
tabindex: true
});
})
.controller('formsController', function($scope){
$scope.checked = false;
$scope.toggleCheckbox = function(){
$scope.checked = !$scope.checked;
}
})
.directive('someCheckbox', function(){
return {
restrict: 'A',
link: function($scope, $el, $attrs) {
$el.on('keypress', function(event){
event.preventDefault();
if(event.keyCode === 32 || event.keyCode === 13){
$scope.toggleCheckbox();
$scope.$apply();
}
});
}
}
})
.directive('showAttrs', function() {
return function(scope, el, attrs) {
var pre = document.createElement('pre');
el.after(pre);
scope.$watch(function() {
var attrs = {};
Array.prototype.slice.call(el[0].attributes, 0).forEach(function(item) {
if (item.name !== 'show-attrs') {
attrs[item.name] = item.value;
}
});
return attrs;
}, function(newAttrs, oldAttrs) {
pre.textContent = JSON.stringify(newAttrs, null, 2);
}, true);
}
});
</script>
</file>
</example>
##Common Accessibility Patterns
Accessibility best practices that apply to web apps in general also apply to Angular.
* [A11Y Project](http://a11yproject.com/)
* [WebAim](http://webaim.org/)
* [Using WAI-ARIA in HTML](http://www.w3.org/TR/2014/WD-aria-in-html-20140626/)
* [Apps For All: Coding Accessible Web Applications](https://shop.smashingmagazine.com/apps-for-all-coding-accessible-web-applications.html)
* **Text alternatives**: Add alternate text content to make visual information accessible using
[these W3C guidelines](http://www.w3.org/TR/html-alt-techniques/). The appropriate technique
depends on the specific markup but can be accomplished using offscreen spans, `aria-label` or
label elements, image `alt` attributes, `figure`/`figcaption` elements and more.
* **HTML Semantics**: If you're creating custom element directives, Web Components or HTML in
general, use native elements wherever possible to utilize built-in events and properties.
Alternatively, use ARIA to communicate semantic meaning. See [notes on ARIA use](http://www.w3.org/TR/aria-in-html/#notes-on-aria-use-in-html).
* **Focus management**: Guide the user around the app as views are appended/removed.
Focus should *never* be lost, as this causes unexpected behavior and much confusion (referred to
as "freak-out mode").
* **Announcing changes**: When filtering or other UI messaging happens away from the user's focus,
notify with [ARIA Live Regions](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions).
* **Color contrast and scale**: Make sure content is legible and interactive controls are usable
at all screen sizes. Consider configurable UI themes for people with color blindness, low vision
or other visual impairments.
* **Progressive enhancement**: Some users do not browse with JavaScript enabled or do not have
the latest browser. An accessible message about site requirements can inform users and improve
the experience.
## Additional Resources
* [Using ARIA in HTML](http://www.w3.org/TR/aria-in-html/)
* [AngularJS Accessibility at ngEurope](https://www.youtube.com/watch?v=dmYDggEgU-s&list=UUEGUP3TJJfMsEM_1y8iviSQ)
* [Testing with Screen Readers](http://webaim.org/articles/screenreader_testing/)
* [Chrome Accessibility Developer Tools](https://chrome.google.com/webstore/detail/accessibility-developer-t/fpkknkljclfencbdbgkenhalefipecmb?hl=en)
* [W3C Accessibility Testing](http://www.w3.org/wiki/Accessibility_testing)
* [WebAIM](http://webaim.org)
* [A11y Project](http://a11yproject.com)
+1 -1
View File
@@ -145,7 +145,7 @@ This is the sequence that your code should follow:
## Deferred Bootstrap
This feature enables tools like [Batarang](github.com/angular/angularjs-batarang) and test runners
This feature enables tools like [Batarang](https://github.com/angular/angularjs-batarang) and test runners
to hook into angular's bootstrap process and sneak in more modules
into the DI registry which can replace or augment DI services for
the purpose of instrumentation or mocking out heavy dependencies.
+1 -1
View File
@@ -332,7 +332,7 @@ The first issue we have to solve is that the dialog box template expects `title`
But we would like the template's scope property `title` to be the result of interpolating the
`<dialog>` element's `title` attribute (i.e. `"Hello {{username}}"`). Furthermore, the buttons expect
the `onOk` and `onCancel` functions to be present in the scope. This limits the usefulness of the
widget. To solve the mapping issue we use the `locals` to create local variables which the template
widget. To solve the mapping issue we use the `scope` to create local variables which the template
expects as follows:
```js
+7 -7
View File
@@ -56,10 +56,10 @@ 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 so called <a name="compiler">"{@link compiler compiler}"</a>.
processes this new markup from the template using the so-called <a name="compiler">"{@link compiler compiler}"</a>.
The loaded, transformed and rendered DOM is then called the <a name="view">"view"</a>.
The first kind of new markup are the so called <a name="directive">"{@link directive directives}"</a>.
The first kind of new markup are the so-called <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
{@link ng.directive:ngApp `ng-app`} attribute, which is linked to a directive that automatically
initializes our application. Angular also defines a directive for the {@link ng.directive:input `input`}
@@ -89,7 +89,7 @@ A filter formats the value of an expression for display to the user.
In the example above, the filter {@link ng.filter:currency `currency`} formats a number
into an output that looks like money.
The important thing in the example is that angular provides _live_ bindings:
The important thing in the example is that Angular provides _live_ bindings:
Whenever the input values change, the value of the expressions are automatically
recalculated and the DOM is updated with their values.
The concept behind this is <a name="databinding">"{@link databinding two-way data binding}"</a>.
@@ -150,13 +150,13 @@ different currencies and also pay the invoice.
What changed?
First, there is a new JavaScript file that contains a so called <a name="controller">"{@link controller controller}"</a>.
First, there is a new JavaScript file that contains a so-called <a name="controller">"{@link controller controller}"</a>.
More exactly, the file contains a constructor function that creates the actual controller instance.
The purpose of controllers is to expose variables and functionality to expressions and directives.
Besides the new file that contains the controller code we also added a
Besides the new file that contains the controller code we also added an
{@link ng.directive:ngController `ng-controller`} directive to the HTML.
This directive tells angular that the new `InvoiceController` is responsible for the element with the directive
This directive tells Angular that the new `InvoiceController` is responsible for the element with the directive
and all of the element's children.
The syntax `InvoiceController as invoice` tells Angular to instantiate the controller
and save it in the variable `invoice` in the current scope.
@@ -263,7 +263,7 @@ services, ...) is created and wired using dependency injection. Within Angular,
the DI container is called the <a name="injector">"{@link di injector}"</a>.
To use DI, there needs to be a place where all the things that should work together are registered.
In Angular, this is the purpose of the so called <a name="module">"{@link module modules}"</a>.
In Angular, this is the purpose of the so-called <a name="module">"{@link module modules}"</a>.
When Angular starts, it will use the configuration of the module with the name defined by the `ng-app` directive,
including the configuration of all modules that this module depends on.
+2 -2
View File
@@ -27,8 +27,8 @@ Protractor is a [Node.js](http://nodejs.org) program, and runs end to end tests
written in JavaScript and run with node. Protractor uses [WebDriver](https://code.google.com/p/selenium/wiki/GettingStarted)
to control browsers and simulate user actions.
For more information on Protractor, view [getting started](https://github.com/angular/protractor/blob/master/docs/getting-started.md)
or the [api docs](https://github.com/angular/protractor/blob/master/docs/api.md).
For more information on Protractor, view [getting started](http://angular.github.io/protractor/#/getting-started)
or the [api docs](http://angular.github.io/protractor/#/api).
Protractor uses [Jasmine](http://jasmine.github.io/1.3/introduction.html) for its test syntax.
As in unit testing, a test file is comprised of one or
+7 -1
View File
@@ -164,6 +164,12 @@ expression. The reason behind this is core to the Angular philosophy that applic
be in controllers, not the views. If you need a real conditional, loop, or to throw from a view
expression, delegate to a JavaScript method instead.
## No RegExp creation with literal notation
You can't create regular expressions from within AngularJS expressions. This is 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`
Directives like {@link ng.directive:ngClick `ngClick`} and {@link ng.directive:ngFocus `ngFocus`}
@@ -314,7 +320,7 @@ someModule.directive('someDirective', function() {
```
```html
<div some-directive name=::myName color=My color is {{::myColor}}></div>
<div some-directive name="::myName" color="My color is {{::myColor}}"></div>
```
+5
View File
@@ -91,6 +91,11 @@ The filter function should be a [pure function](http://en.wikipedia.org/wiki/Pur
means that it should be stateless and idempotent. Angular relies on these properties and executes
the filter only when the inputs to the function change.
<div class="alert alert-warning">
**Note:** filter names must be valid angular expression identifiers, such as `uppercase` or `orderBy`.
Names with special characters, such as hyphens and dots, are not allowed.
</div>
The following sample filter reverses a text string. In addition, it conditionally makes the
text upper-case.
+3
View File
@@ -56,6 +56,9 @@ for other directives to augment its behavior.
Note that `novalidate` is used to disable browser's native form validation.
The value of `ngModel` won't be set unless it passes validation for the input field.
For example: inputs of type `email` must have a value in the form of `user@domain`.
# Using CSS classes
+1 -1
View File
@@ -83,7 +83,7 @@ To make your Angular application work on IE please make sure that:
</body>
</html>
```
5. Use `ng-style` tags instead of `style="{{ someCss }}"`. The later works in Chrome and Firefox
5. Use `ng-style` tags instead of `style="{{ someCss }}"`. The latter works in Chrome and Firefox
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
+3 -1
View File
@@ -41,9 +41,10 @@ In Angular applications, you move the job of filling page templates with data fr
### Other AngularJS Features
* **Animation:** {@link guide/animations Core concepts}, {@link ngAnimate ngAnimate API}, and [Animation in AngularJS 1.2](http://www.yearofmoo.com/2013/08/remastered-animation-in-angularjs-1-2.html)
* **Security:** {@link ng.$sce Strict Contextual Escaping}, {@link ng.directive:ngCsp Content Security Policy}, {@link ngSanitize.$sanitize $sanitize}, [video](https://www.youtube.com/watch?v=18ifoT-Id54)
* **Security:** {@link guide/security Security Docs}, {@link ng.$sce Strict Contextual Escaping}, {@link ng.directive:ngCsp Content Security Policy}, {@link ngSanitize.$sanitize $sanitize}, [video](https://www.youtube.com/watch?v=18ifoT-Id54)
* **Internationalization and Localization:** {@link guide/i18n Angular Guide to i18n and l10n}, {@link ng.filter:date date filter}, {@link ng.filter:currency currency filter}, [Creating multilingual support](http://www.novanet.no/blog/hallstein-brotan/dates/2013/10/creating-multilingual-support-using-angularjs/)
* **Mobile:** {@link ngTouch Touch events}
* **Accessibility:** {@link guide/accessibility ngAria}
### Testing
@@ -107,6 +108,7 @@ This is a short list of libraries with specific support and documentation for wo
* [Developing an AngularJS Edge](http://www.amazon.com/Developing-AngularJS-Edge-Christopher-Hiller-ebook/dp/B00CJLFF8K) by Christopher Hiller
* [ng-book: The Complete Book on AngularJS](http://ng-book.com/) by Ari Lerner
* [AngularJS : Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
* [AngularJS UI Development](http://www.amazon.com/AngularJS-UI-Development-Amit-Ghart-ebook/dp/B00OXVAK7A) by Amit Gharat and Matthias Nehlsen
###Videos:
* [egghead.io](http://egghead.io/)
+1 -1
View File
@@ -23,7 +23,7 @@ This is to disallow changing the behaviour of existing functions
in an unforseen fashion.
- due to [6081f207](https://github.com/angular/angular.js/commit/6081f20769e64a800ee8075c168412b21f026d99),
The (deprecated) __proto__ propery does not work inside angular expressions
The (deprecated) __proto__ property does not work inside angular expressions
anymore.
- due to [48fa3aad](https://github.com/angular/angular.js/commit/48fa3aadd546036c7e69f71046f659ab1de244c6),
+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 collection of two kinds
during the bootstrap process. In its simplest form the module consist of a collection of two kinds
of blocks:
1. **Configuration blocks** - get executed during the provider registrations and configuration
+32
View File
@@ -41,3 +41,35 @@ The page should reload and the debug information should now be available.
For more see the docs pages on {@link ng.$compileProvider#debugInfoEnabled `$compileProvider`}
and {@link angular.reloadWithDebugInfo `angular.reloadWithDebugInfo`}.
## Strict DI Mode
Using strict di mode in your production application will throw errors when a injectable
function is not
{@link di#dependency-annotation annotated explicitly}. Strict di mode is intended to help
you make sure that your code will work when minified. However, it also will force you to
make sure that your injectable functions are explicitly annotated which will improve
angular's performance when injecting dependencies in your injectable functions because it
doesn't have to dynamically discover a function's dependencies. It is recommended to
automate the explicit annotation via a tool like
[ng-annotate](https://github.com/olov/ng-annotate) when you deploy to production (and enable
strict di mode)
To enable strict di mode, you have two options:
```html
<div ng-app="myApp" ng-strict-di>
<!-- your app here -->
</div>
```
or
```js
angular.bootstrap(document, ['myApp'], {
strictDi: true
});
```
For more information, see the
{@link di#using-strict-dependency-injection DI Guide}.
+12
View File
@@ -339,6 +339,18 @@ the dirty checking function must be efficient. Care should be taken that the dir
function does not do any DOM access, as DOM access is orders of magnitude slower than property
access on JavaScript object.
### Scope `$watch` Depths
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-scope-watch-strategies.png">
Dirty checking can be done with three strategies: By reference, by collection contents, and by value. The strategies differ in the kinds of changes they detect, and in their performance characteristics.
- Watching *by reference* ({@link
ng.$rootScope.Scope#$watch scope.$watch} `(watchExpression, listener)`) detects a change when the whole value returned by the watch expression switches to a new value. If the value is an array or an object, changes inside it are not detected. This is the most efficient stategy.
- Watching *collection contents* ({@link
ng.$rootScope.Scope#$watchCollection scope.$watchCollection} `(watchExpression, listener)`) detects changes that occur inside an array or an object: When items are added, removed, or reordered. The detection is shallow - it does not reach into nested collections. Watching collection contents is more expensive than watching by reference, because copies of the collection contents need to be maintained. However, the strategy attempts to minimize the amount of copying required.
- Watching *by value* ({@link
ng.$rootScope.Scope#$watch scope.$watch} `(watchExpression, listener, true)`) detects any change in an arbitrarily nested data structure. It is the most powerful change detection strategy, but also the most expensive. A full traversal of the nested data structure is needed on each digest, and a full copy of it needs to be held in memory.
## Integration with the browser event loop
<img class="pull-right" style="padding-left: 3em; padding-bottom: 1em;" src="img/guide/concepts-runtime.png">
+60
View File
@@ -0,0 +1,60 @@
@ngdoc overview
@name Security
@sortOrder 525
@description
# Security
This document explains some of AngularJS's security features and best practices that you should
keep in mind as you build your application.
## Expression Sandboxing
AngularJS's expressions are sandboxed not for security reasons, but instead to maintain a proper
separation of application responsibilities. For example, access to `window` is disallowed
because it makes it easy to introduce brittle global state into your application.
However, this sandbox is not intended to stop attackers who can edit the template before it's
processed by Angular. It may be possible to run arbitrary JavaScript inside double-curly bindings
if an attacker can modify them.
But if an attacker can change arbitrary HTML templates, there's nothing stopping them from doing:
```html
<script>somethingEvil();</script>
```
It's better to design your application in such a way that users cannot change client-side templates.
For instance:
* Do not mix client and server templates
* Do not use user input to generate templates dynamically
* Do not run user input through `$scope.$eval`
* Consider using {@link ng.directive:ngCsp CSP} (but don't rely only on CSP)
## Mixing client-side and server-side templates
In general, we recommend against this because it can create unintended XSS vectors.
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.
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.
## Reporting a security issue
Email us at [security@angularjs.org](mailto:security@angularjs.org) to report any potential
security issues in AngularJS.
Please keep in mind the above points about Angular's expression language.
## See also
* {@link ng.directive:ngCsp Content Security Policy}
* {@link ng.$sce Strict Contextual Escaping}
* {@link ngSanitize.$sanitize $sanitize}
+216 -223
View File
@@ -8,15 +8,15 @@ comes with almost no help from the compiler. For this reason we feel very strong
written in JavaScript needs to come with a strong set of tests. We have built many features into
Angular which makes testing your Angular applications easy. So there is no excuse for not testing.
# Separation of Concerns
## Separation of Concerns
Unit testing as the name implies is about testing individual units of code. Unit tests try to
Unit testing, as the name implies, is about testing individual units of code. Unit tests try to
answer questions such as "Did I think about the logic correctly?" or "Does the sort function order
the list in the right order?"
In order to answer such a question it is very important that we can isolate the unit of code under test.
That is because when we are testing the sort function we don't want to be forced into creating
related pieces such as the DOM elements, or making any XHR calls in getting the data to sort.
related pieces such as the DOM elements, or making any XHR calls to fetch the data to sort.
While this may seem obvious it can be very difficult to call an individual function on a
typical project. The reason is that the developers often mix concerns resulting in a
@@ -24,12 +24,10 @@ piece of code which does everything. It makes an XHR request, it sorts the respo
manipulates the DOM.
With Angular we try to make it easy for you to do the right thing, and so we
provide dependency injection for your XHR (which you can mock out) and we created abstractions which
allow you to sort your model without having to resort to manipulating the DOM. So that in the end,
it is easy to write a sort function which sorts some data, so that your test can create a data set,
apply the function, and assert that the resulting model is in the correct order. The test does not
have to wait for the XHR response to arrive, create the right kind of test DOM, nor assert that your
function has mutated the DOM in the right way.
provide dependency injection for your XHR requests, which can be mocked, and we provide abstractions which
allow you to test your model without having to resort to manipulating the DOM. The test can then
assert that the data has been sorted without having to create or look at the state of the DOM or
wait for any XHR requests to return data. The individual sort function can be tested in isolation.
## With great power comes great responsibility
@@ -38,230 +36,218 @@ We tried to make the right thing easy, but if you ignore these guidelines you ma
untestable application.
## Dependency Injection
There are several ways in which you can get a hold of a dependency. You can:
1. Create it using the `new` operator.
2. Look for it in a well-known place, also known as a global singleton.
3. Ask a registry (also known as service registry) for it. (But how do you get a hold of
the registry? Most likely by looking it up in a well known place. See #2.)
4. Expect it to be handed to you.
Out of the four options in the list above, only the last one is testable. Let's look at why:
Angular comes with {@link di dependency injection} built-in, which makes testing components much
easier, because you can pass in a component's dependencies and stub or mock them as you wish.
### Using the `new` operator
Components that have their dependencies injected allow them to be easily mocked on a test by
test basis, without having to mess with any global variables that could inadvertently affect
another test.
While there is nothing wrong with the `new` operator fundamentally, a problem arises when calling `new`
on a constructor. This permanently binds the call site to the type. For example, let's say that we try to
instantiate an `XHR` that will retrieve data from the server.
## Additional tools for testing Angular applications
For testing Angular applications there are certain tools that you should use that will make testing much
easier to set up and run.
### Karma
[Karma](http://karma-runner.github.io/) is a JavaScript command line tool that can be used to spawn
a web server which loads your application's source code and executes your tests. You can configure
Karma to run against a number of browsers, which is useful for being confident that your application
works on all browsers you need to support. Karma is executed on the command line and will display
the results of your tests on the command line once they have run in the browser.
Karma is a NodeJS application, and should be installed through npm. Full installation instructions
are available on [the Karma website](http://karma-runner.github.io/0.12/intro/installation.html).
### Jasmine
[Jasmine](http://jasmine.github.io/1.3/introduction.html) is a test driven development framework for
JavaScript that has become the most popular choice for testing Angular applications. Jasmine
provides functions to help with structuring your tests and also making assertions. As your tests
grow, keeping them well structured and documented is vital, and Jasmine helps achieve this.
In Jasmine we use the `describe` function to group our tests together:
```js
function MyClass() {
this.doWork = function() {
var xhr = new XHR();
xhr.open(method, url, true);
xhr.onreadystatechange = function() {...}
xhr.send();
}
}
describe("sorting the list of users", function() {
// individual tests go here
});
```
A problem surfaces in tests when we would like to instantiate a `MockXHR` that would
allow us to return fake data and simulate network failures. By calling `new XHR()` we are
permanently bound to the actual XHR and there is no way to replace it. Yes, we could monkey
patch, but that is a bad idea for many reasons which are outside the scope of this document.
Here's an example of how the class above becomes hard to test when resorting to monkey patching:
And then each individual test is defined within a call to the `it` function:
```js
var oldXHR = XHR;
XHR = function MockXHR() {};
var myClass = new MyClass();
myClass.doWork();
// assert that MockXHR got called with the right arguments
XHR = oldXHR; // if you forget this bad things will happen
describe('sorting the list of users', function() {
it('sorts in descending order by default', function() {
// your test assertion goes here
});
});
```
Grouping related tests within `describe` blocks and describing each individual test within an
`it` call keeps your tests self documenting.
### Global look-up:
Another way to approach the problem is to look for the service in a well-known location.
Finally, Jasmine provides matchers which let you make assertions:
```js
function MyClass() {
this.doWork = function() {
global.xhr({
method:'...',
url:'...',
complete:function(response){ ... }
})
}
}
describe('sorting the list of users', function() {
it('sorts in descending order by default', function() {
var users = ['jack', 'igor', 'jeff'];
var sorted = sortUsers(users);
expect(sorted).toEqual(['jeff', 'jack', 'igor']);
});
});
```
While no new dependency instance is created, it is fundamentally the same as `new` in
that no way exists to intercept the call to `global.xhr` for testing purposes, other than
through monkey patching. The basic issue for testing is that a global variable needs to be mutated in
order to replace it with call to a mock method. For further explanation of why this is bad see: [Brittle Global
State & Singletons](http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/)
Jasmine comes with a number of matchers that help you make a variety of assertions. You should [read
the Jasmine documentation](http://jasmine.github.io/1.3/introduction.html#section-Matchers) to see
what they are. To use Jasmine with Karma, we use the
[karma-jasmine](https://github.com/karma-runner/karma-jasmine) test runner.
The class above is hard to test since we have to change the global state:
### angular-mocks
Angular also provides the {@link ngMock} module, which provides mocking for your tests. This is used
to inject and mock Angular services within unit tests. In addition, it is able to extend other
modules so they are synchronous. Having tests synchronous keeps them much cleaner and easier to work
with. One of the most useful parts of ngMock is {@link ngMock.$httpBackend}, which lets us mock XHR
requests in tests, and return sample data instead.
## Testing a Controller
Because Angular separates logic from the view layer, it keeps controllers easy to test. Let's take a
look at how we might test the controller below, which provides `$scope.grade`, which sets a property
on the scope based on the length of the password.
```js
var oldXHR = global.xhr;
global.xhr = function mockXHR() {};
var myClass = new MyClass();
myClass.doWork();
// assert that mockXHR got called with the right arguments
global.xhr = oldXHR; // if you forget this bad things will happen
angular.module('app', [])
.controller('PasswordController', function PasswordController($scope) {
$scope.password = '';
$scope.grade = function() {
var size = $scope.password.length;
if (size > 8) {
$scope.strength = 'strong';
} else if (size > 3) {
$scope.strength = 'medium';
} else {
$scope.strength = 'weak';
}
};
});
```
### Service Registry:
It may seem that this can be solved by having a registry of all the services and then
having the tests replace the services as needed.
Because controllers are not available on the global scope, we need to use {@link
angular.mock.inject} to inject our controller first. The first step is to use the `module` function,
which is provided by angular-mocks. This loads in the module it's given, so it is available in your
tests. We pass this into `beforeEach`, which is a function Jasmine provides that lets us run code
before each test. Then we can use `inject` to access `$controller`, the service that is responsible
for instantiating controllers.
```js
function MyClass() {
var serviceRegistry = ????;
this.doWork = function() {
var xhr = serviceRegistry.get('xhr');
xhr({
method:'...',
url:'...',
complete:function(response){ ... }
})
}
describe('PasswordController', function() {
beforeEach(module('app'));
var $controller;
beforeEach(inject(function(_$controller_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$controller = _$controller_;
}));
describe('$scope.grade', function() {
it('sets the strength to "strong" if the password length is >8 chars', function() {
var $scope = {};
var controller = $controller('PasswordController', { $scope: $scope });
$scope.password = 'longerthaneightchars';
$scope.grade();
expect($scope.strength).toEqual('strong');
});
});
});
```
However, where does the serviceRegistry come from? If it is:
* `new`-ed up, the test has no chance to reset the services for testing.
* a global look-up then the service returned is global as well (but resetting is easier, since
only one global variable exists to be reset).
The class above is hard to test since we have to change the global state:
Notice how by nesting the `describe` calls and being descriptive when calling them with strings, the
test is very clear. It documents exactly what it is testing, and at a glance you can quickly see
what is happening. Now let's add the test for when the password is less than three characters, which
should see `$scope.strength` set to "weak":
```js
var oldServiceLocator = global.serviceLocator;
global.serviceLocator.set('xhr', function mockXHR() {});
var myClass = new MyClass();
myClass.doWork();
// assert that mockXHR got called with the right arguments
global.serviceLocator = oldServiceLocator; // if you forget this bad things will happen
describe('PasswordController', function() {
beforeEach(module('app'));
var $controller;
beforeEach(inject(function(_$controller_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$controller = _$controller_;
}));
describe('$scope.grade', function() {
it('sets the strength to "strong" if the password length is >8 chars', function() {
var $scope = {};
var controller = $controller('PasswordController', { $scope: $scope });
$scope.password = 'longerthaneightchars';
$scope.grade();
expect($scope.strength).toEqual('strong');
});
it('sets the strength to "weak" if the password length <3 chars', function() {
var $scope = {};
var controller = $controller('PasswordController', { $scope: $scope });
$scope.password = 'a';
$scope.grade();
expect($scope.strength).toEqual('weak');
});
});
});
```
### Passing in Dependencies:
Last, the dependency can be passed in.
Now we have two tests, but notice the duplication between the tests. Both have to
create the `$scope` variable and create the controller. As we add new tests, this duplication is
only going to get worse. Thankfully, Jasmine provides `beforeEach`, which lets us run a function
before each individual test. Let's see how that would tidy up our tests:
```js
function MyClass(xhr) {
this.doWork = function() {
xhr({
method:'...',
url:'...',
complete:function(response){ ... }
})
}
describe('PasswordController', function() {
beforeEach(module('app'));
var $controller;
beforeEach(inject(function(_$controller_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$controller = _$controller_;
}));
describe('$scope.grade', function() {
var $scope, controller;
beforeEach(function() {
$scope = {};
controller = $controller('PasswordController', { $scope: $scope });
});
it('sets the strength to "strong" if the password length is >8 chars', function() {
$scope.password = 'longerthaneightchars';
$scope.grade();
expect($scope.strength).toEqual('strong');
});
it('sets the strength to "weak" if the password length <3 chars', function() {
$scope.password = 'a';
$scope.grade();
expect($scope.strength).toEqual('weak');
});
});
});
```
This is the preferred method since the code makes no assumptions about the origin of `xhr` and cares
instead about whoever created the class responsible for passing it in. Since the creator of the
class should be different code than the user of the class, it separates the responsibility of
creation from the logic. This is dependency-injection in a nutshell.
We've moved the duplication out and into the `beforeEach` block. Each individual test now
only contains the code specific to that test, and not code that is general across all tests. As you
expand your tests, keep an eye out for locations where you can use `beforeEach` to tidy up tests.
`beforeEach` isn't the only function of this sort that Jasmine provides, and the [documentation
lists the others](http://jasmine.github.io/1.3/introduction.html#section-Setup_and_Teardown).
The class above is testable, since in the test we can write:
```js
function xhrMock(args) {...}
var myClass = new MyClass(xhrMock);
myClass.doWork();
// assert that xhrMock got called with the right arguments
```
Notice that no global variables were harmed in the writing of this test.
Angular comes with {@link di dependency injection} built-in, making the right thing
easy to do, but you still need to do it if you wish to take advantage of the testability story.
## Controllers
What makes each application unique is its logic, and the logic is what we would like to test. If the logic
for your application contains DOM manipulation, it will be hard to test. See the example
below:
```js
function PasswordCtrl() {
// get references to DOM elements
var msg = $('.ex1 span');
var input = $('.ex1 input');
var strength;
this.grade = function() {
msg.removeClass(strength);
var pwd = input.val();
password.text(pwd);
if (pwd.length > 8) {
strength = 'strong';
} else if (pwd.length > 3) {
strength = 'medium';
} else {
strength = 'weak';
}
msg
.addClass(strength)
.text(strength);
}
}
```
The code above is problematic from a testability point of view since it requires your test to have the right kind
of DOM present when the code executes. The test would look like this:
```js
var input = $('<input type="text"/>');
var span = $('<span>');
$('body').html('<div class="ex1">')
.find('div')
.append(input)
.append(span);
var pc = new PasswordCtrl();
input.val('abc');
pc.grade();
expect(span.text()).toEqual('weak');
$('body').empty();
```
In angular the controllers are strictly separated from the DOM manipulation logic and this results in
a much easier testability story as the following example shows:
```js
function PasswordCtrl($scope) {
$scope.password = '';
$scope.grade = function() {
var size = $scope.password.length;
if (size > 8) {
$scope.strength = 'strong';
} else if (size > 3) {
$scope.strength = 'medium';
} else {
$scope.strength = 'weak';
}
};
}
```
and the test is straight forward:
```js
var $scope = {};
var pc = $controller('PasswordCtrl', { $scope: $scope });
$scope.password = 'abc';
$scope.grade();
expect($scope.strength).toEqual('weak');
```
Notice that the test is not only much shorter, it is also easier to follow what is happening. We say
that such a test tells a story, rather than asserting random bits which don't seem to be related.
## Filters
## Testing Filters
{@link ng.$filterProvider Filters} are functions which transform the data into a user readable
format. They are important because they remove the formatting responsibility from the application
logic, further simplifying the application logic.
@@ -273,12 +259,20 @@ myModule.filter('length', function() {
}
});
var length = $filter('length');
expect(length(null)).toEqual(0);
expect(length('abc')).toEqual(3);
describe('length filter', function() {
it('returns 0 when given null', function() {
var length = $filter('length');
expect(length(null)).toEqual(0);
});
it('returns the correct value when given a string of chars', function() {
var length = $filter('length');
expect(length('abc')).toEqual(3);
});
});
```
## Directives
## Testing Directives
Directives in angular are responsible for encapsulating complex functionality within custom HTML tags,
attributes, classes or comments. Unit tests are very important for directives because the components
you create with directives may be used throughout your application and in many different contexts.
@@ -309,28 +303,28 @@ verify this functionality. Note that the expression `{{1 + 1}}` times will also
```js
describe('Unit testing great quotes', function() {
var $compile;
var $rootScope;
var $compile,
$rootScope;
// Load the myApp module, which contains the directive
beforeEach(module('myApp'));
// Load the myApp module, which contains the directive
beforeEach(module('myApp'));
// Store references to $rootScope and $compile
// so they are available to all tests in this describe block
beforeEach(inject(function(_$compile_, _$rootScope_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
// Store references to $rootScope and $compile
// so they are available to all tests in this describe block
beforeEach(inject(function(_$compile_, _$rootScope_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
it('Replaces the element with the appropriate content', function() {
// Compile a piece of HTML containing the directive
var element = $compile("<a-great-eye></a-great-eye>")($rootScope);
// fire all the watches, so the scope expression {{1 + 1}} will be evaluated
$rootScope.$digest();
// Check that the compiled element contains the templated content
expect(element.html()).toContain("lidless, wreathed in flame, 2 times");
});
it('Replaces the element with the appropriate content', function() {
// Compile a piece of HTML containing the directive
var element = $compile("<a-great-eye></a-great-eye>")($rootScope);
// fire all the watches, so the scope expression {{1 + 1}} will be evaluated
$rootScope.$digest();
// Check that the compiled element contains the templated content
expect(element.html()).toContain("lidless, wreathed in flame, 2 times");
});
});
```
@@ -431,4 +425,3 @@ Otherwise you may run into issues if the test directory hierarchy differs from t
## Sample project
See the [angular-seed](https://github.com/angular/angular-seed) project for an example.
+1 -1
View File
@@ -116,7 +116,7 @@ npm --version
```
<div class="alert alert-info">If you need to run a different versions of node.js
<div class="alert alert-info">If you need to run different versions of node.js
in your local environment, consider installing
<a href="https://github.com/creationix/nvm" title="Node Version Manager Github Repo link">
Node Version Manager (nvm)
+1 -1
View File
@@ -120,7 +120,7 @@ really is that easy to set up any functional, readable, end-to-end test.
### Running End to End Tests with Protractor
Even though the syntax of this test looks very much like our controller unit test written with
Jasmine, the end-to-end test uses APIs of [Protractor](https://github.com/angular/protractor). Read
about the Protractor APIs at https://github.com/angular/protractor/blob/master/docs/api.md.
about the Protractor APIs at http://angular.github.io/protractor/#/api.
Much like Karma is the test runner for unit tests, we use Protractor to run end-to-end tests.
Try it with `npm run protractor`. End-to-end tests are slow, so unlike with unit tests, Protractor
+1 -1
View File
@@ -182,7 +182,7 @@ You can now rerun `npm run protractor` to see the tests run.
# Experiments
* In the `PhoneListCtrl` controller, remove the statement that sets the `orderProp` value and
you'll see that Angular will temporarily add a new "unknown" option to the drop-down list and the
you'll see that Angular will temporarily add a new blank ("unknown") option to the drop-down list and the
ordering will default to unordered/natural order.
* Add an `{{orderProp}}` binding into the `index.html` template to display its current value as
+5 -3
View File
@@ -236,7 +236,9 @@ the response is received:
```
* We flush the request queue in the browser by calling `$httpBackend.flush()`. This causes the
promise returned by the `$http` service to be resolved with the trained response.
promise returned by the `$http` service to be resolved with the trained response. See
'Flushing HTTP requests' in the {@link ngMock.$httpBackend mock $httpBackend} documentation for
a full explanation of why this is necessary.
* We make the assertions, verifying that the phone model now exists on the scope.
@@ -256,8 +258,8 @@ You should now see the following output in the Karma tab:
# Experiments
* At the bottom of `index.html`, add a `<pre>{{phones | json}}</pre>` binding to see the list of phones
displayed in json format.
* At the bottom of `index.html`, add a `<pre>{{phones | filter:query | orderBy:orderProp | json}}</pre>`
binding to see the list of phones displayed in json format.
* In the `PhoneListCtrl` controller, pre-process the http response by limiting the number of phones
to the first 5 in the list. Use the following code in the `$http` callback:
+1 -1
View File
@@ -184,7 +184,7 @@ You can now rerun `npm run protractor` to see the tests run.
# Experiments
* Using the [Protractor API](https://github.com/angular/protractor/blob/master/docs/api.md),
* Using the [Protractor API](http://angular.github.io/protractor/#/api),
write a test that verifies that we display 4 thumbnail images on the Nexus S details page.
+4 -3
View File
@@ -9,8 +9,8 @@
In this final step, we will enhance our phonecat web application by attaching CSS and JavaScript
animations on top of the template code we created before.
* Used the `ngAnimate` to enable animations throughout the application.
* Common `ng` directives automatically trigger hooks for animations to tap into.
* We now use the `ngAnimate` module to enable animations throughout the application.
* We also use common `ng` directives to automatically trigger hooks for animations to tap into.
* When an animation is found then the animation will run in between the standard DOM operation that
is being issued on the element at the given time (e.g. inserting and removing nodes on
{@link ngRepeat `ngRepeat`} or adding and removing classes on
@@ -369,7 +369,8 @@ occur whenever the CSS class itself changes.
Whenever a new phone thumbnail is selected, the state changes and the `.active` CSS class is added
to the matching profile image and the animation plays.
Let's get started and tweak our HTML code on the `phone-detail.html` page first:
Let's get started and tweak our HTML code on the `phone-detail.html` page first. Notice that we
have changed the way we display our large image:
__`app/partials/phone-detail.html`.__
Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

+2 -2
View File
@@ -267,7 +267,7 @@ goog.i18n.currency.adjustPrecision = function(pattern, currencyCode) {
* 18: two decimals precision (2), currency sign last (16), no space (0)
* 50: two decimals precision (2), currency sign last (16), space (32)
*
* @type {!Object.<!Array.<?>>}
* @const {!Object<!Array<?>>}
*/
goog.i18n.currency.CurrencyInfo = {
'AED': [2, 'dh', '\u062f.\u0625.', 'DH'],
@@ -334,7 +334,7 @@ goog.i18n.currency.CurrencyInfo = {
/**
* Tier 2 currency information.
* @type {!Object.<!Array.<?>>}
* @const {!Object<!Array<?>>}
*/
goog.i18n.currency.CurrencyInfoTier2 = {
'AFN': [48, 'Af.', 'AFN'],
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+20 -32
View File
@@ -18,7 +18,7 @@
* This file is autogenerated by script:
* http://go/generate_number_constants.py
* using the --for_closure flag.
* File generated from CLDR ver. 25
* File generated from CLDR ver. 26
*
* To reduce the file size (which may cause issues in some JS
* developing environments), this file will only contain locales
@@ -42,7 +42,6 @@ goog.provide('goog.i18n.NumberFormatSymbols_am_ET');
goog.provide('goog.i18n.NumberFormatSymbols_ar');
goog.provide('goog.i18n.NumberFormatSymbols_ar_001');
goog.provide('goog.i18n.NumberFormatSymbols_az');
goog.provide('goog.i18n.NumberFormatSymbols_az_Cyrl_AZ');
goog.provide('goog.i18n.NumberFormatSymbols_az_Latn_AZ');
goog.provide('goog.i18n.NumberFormatSymbols_bg');
goog.provide('goog.i18n.NumberFormatSymbols_bg_BG');
@@ -364,13 +363,6 @@ goog.i18n.NumberFormatSymbols_az = {
};
/**
* Number formatting symbols for locale az_Cyrl_AZ.
* @enum {string}
*/
goog.i18n.NumberFormatSymbols_az_Cyrl_AZ = goog.i18n.NumberFormatSymbols_az;
/**
* Number formatting symbols for locale az_Latn_AZ.
* @enum {string}
@@ -455,8 +447,8 @@ goog.i18n.NumberFormatSymbols_br = {
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'EUR'
};
@@ -1063,7 +1055,7 @@ goog.i18n.NumberFormatSymbols_es = {
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '#,##0.00\u00A0\u00A4',
DEF_CURRENCY_CODE: 'EUR'
};
@@ -1086,7 +1078,7 @@ goog.i18n.NumberFormatSymbols_es_419 = {
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
PERCENT_PATTERN: '#,##0\u00A0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
DEF_CURRENCY_CODE: 'MXN'
};
@@ -1492,7 +1484,7 @@ goog.i18n.NumberFormatSymbols_gu = {
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##,##0.###',
SCIENTIFIC_PATTERN: '#E0',
SCIENTIFIC_PATTERN: '[#E0]',
PERCENT_PATTERN: '#,##,##0%',
CURRENCY_PATTERN: '\u00A4#,##,##0.00',
DEF_CURRENCY_CODE: 'INR'
@@ -1582,7 +1574,7 @@ goog.i18n.NumberFormatSymbols_hi = {
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##,##0.###',
SCIENTIFIC_PATTERN: '#E0',
SCIENTIFIC_PATTERN: '[#E0]',
PERCENT_PATTERN: '#,##,##0%',
CURRENCY_PATTERN: '\u00A4#,##,##0.00',
DEF_CURRENCY_CODE: 'INR'
@@ -1961,7 +1953,7 @@ goog.i18n.NumberFormatSymbols_kn = {
ZERO_DIGIT: '0',
PLUS_SIGN: '+',
MINUS_SIGN: '-',
EXP_SYMBOL: '\u0C88',
EXP_SYMBOL: 'E',
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
@@ -2085,7 +2077,7 @@ goog.i18n.NumberFormatSymbols_lo = {
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN:
'\u0E9A\u0ECD\u0EC8\u0EC1\u0EA1\u0EC8\u0E99\u0EC2\u0E95\u0EC0\u0EA5\u0E81',
'\u0E9A\u0ECD\u0EC8\u200B\u0EC1\u0EA1\u0EC8\u0E99\u200B\u0EC2\u0E95\u200B\u0EC0\u0EA5\u0E81',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#',
PERCENT_PATTERN: '#,##0%',
@@ -2149,7 +2141,7 @@ goog.i18n.NumberFormatSymbols_lv = {
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
CURRENCY_PATTERN: '\u00A4#0.00',
DEF_CURRENCY_CODE: 'EUR'
};
@@ -2208,8 +2200,8 @@ goog.i18n.NumberFormatSymbols_ml = {
NAN: 'NaN',
DECIMAL_PATTERN: '#,##,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##,##0%',
CURRENCY_PATTERN: '#,##,##0.00\u00A4',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
DEF_CURRENCY_CODE: 'INR'
};
@@ -2266,7 +2258,7 @@ goog.i18n.NumberFormatSymbols_mr = {
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
DECIMAL_PATTERN: '#,##,##0.###',
SCIENTIFIC_PATTERN: '[#E0]',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
@@ -2427,7 +2419,7 @@ goog.i18n.NumberFormatSymbols_ne = {
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
CURRENCY_PATTERN: '\u00A4\u00A0#,##0.00',
DEF_CURRENCY_CODE: 'NPR'
};
@@ -2545,7 +2537,7 @@ goog.i18n.NumberFormatSymbols_pa = {
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##,##0.###',
SCIENTIFIC_PATTERN: '#E0',
SCIENTIFIC_PATTERN: '[#E0]',
PERCENT_PATTERN: '#,##,##0%',
CURRENCY_PATTERN: '\u00A4#,##,##0.00',
DEF_CURRENCY_CODE: 'INR'
@@ -2718,7 +2710,7 @@ goog.i18n.NumberFormatSymbols_si = {
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
SCIENTIFIC_PATTERN: '#',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
DEF_CURRENCY_CODE: 'LKR'
@@ -2957,10 +2949,10 @@ goog.i18n.NumberFormatSymbols_te = {
PERMILL: '\u2030',
INFINITY: '\u221E',
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
DECIMAL_PATTERN: '#,##,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00',
CURRENCY_PATTERN: '\u00A4#,##,##0.00',
DEF_CURRENCY_CODE: 'INR'
};
@@ -3102,8 +3094,8 @@ goog.i18n.NumberFormatSymbols_ur = {
NAN: 'NaN',
DECIMAL_PATTERN: '#,##0.###',
SCIENTIFIC_PATTERN: '#E0',
PERCENT_PATTERN: '#,##0%',
CURRENCY_PATTERN: '\u00A4#,##0.00\u200E',
PERCENT_PATTERN: '#,##,##0%',
CURRENCY_PATTERN: '\u00A4\u00A0#,##,##0.00',
DEF_CURRENCY_CODE: 'PKR'
};
@@ -3321,10 +3313,6 @@ if (goog.LOCALE == 'az') {
goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_az;
}
if (goog.LOCALE == 'az_Cyrl_AZ' || goog.LOCALE == 'az-Cyrl-AZ') {
goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_az;
}
if (goog.LOCALE == 'az_Latn_AZ' || goog.LOCALE == 'az-Latn-AZ') {
goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_az;
}
File diff suppressed because it is too large Load Diff
+44 -27
View File
@@ -17,7 +17,7 @@
*
* This file is autogenerated by script:
* http://go/generate_pluralrules.py
* File generated from CLDR ver. 25
* File generated from CLDR ver. 26
*
* Before check in, this file could have been manually edited. This is to
* incorporate changes before we could fix CLDR. All manual modification must be
@@ -409,6 +409,23 @@ goog.i18n.pluralRules.srSelect_ = function(n, opt_precision) {
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for mk locale
*
* @param {number} n The count of items.
* @param {number=} opt_precision Precision for number formatting, if not default.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.mkSelect_ = function(n, opt_precision) {
var i = n | 0;
var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
if (vf.v == 0 && i % 10 == 1 || vf.f % 10 == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for hi locale
*
@@ -426,17 +443,15 @@ goog.i18n.pluralRules.hiSelect_ = function(n, opt_precision) {
};
/**
* Plural select rules for mk locale
* Plural select rules for pt locale
*
* @param {number} n The count of items.
* @param {number=} opt_precision Precision for number formatting, if not default.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.mkSelect_ = function(n, opt_precision) {
var i = n | 0;
var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
if (vf.v == 0 && i % 10 == 1 || vf.f % 10 == 1) {
goog.i18n.pluralRules.ptSelect_ = function(n, opt_precision) {
if (n >= 0 && n <= 2 && n != 2) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
@@ -562,24 +577,6 @@ goog.i18n.pluralRules.akSelect_ = function(n, opt_precision) {
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for pt locale
*
* @param {number} n The count of items.
* @param {number=} opt_precision Precision for number formatting, if not default.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.ptSelect_ = function(n, opt_precision) {
var i = n | 0;
var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
var wt = goog.i18n.pluralRules.get_wt_(vf.v, vf.f);
if (i == 1 && vf.v == 0 || i == 0 && wt.t == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for pl locale
*
@@ -748,6 +745,29 @@ goog.i18n.pluralRules.gdSelect_ = function(n, opt_precision) {
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for dsb locale
*
* @param {number} n The count of items.
* @param {number=} opt_precision Precision for number formatting, if not default.
* @return {goog.i18n.pluralRules.Keyword} Locale-specific plural value.
* @private
*/
goog.i18n.pluralRules.dsbSelect_ = function(n, opt_precision) {
var i = n | 0;
var vf = goog.i18n.pluralRules.get_vf_(n, opt_precision);
if (vf.v == 0 && i % 100 == 1 || vf.f % 100 == 1) {
return goog.i18n.pluralRules.Keyword.ONE;
}
if (vf.v == 0 && i % 100 == 2 || vf.f % 100 == 2) {
return goog.i18n.pluralRules.Keyword.TWO;
}
if (vf.v == 0 && i % 100 >= 3 && i % 100 <= 4 || vf.f % 100 >= 3 && vf.f % 100 <= 4) {
return goog.i18n.pluralRules.Keyword.FEW;
}
return goog.i18n.pluralRules.Keyword.OTHER;
};
/**
* Plural select rules for lv locale
*
@@ -852,9 +872,6 @@ if (goog.LOCALE == 'en_IE' || goog.LOCALE == 'en-IE') {
if (goog.LOCALE == 'en_IN' || goog.LOCALE == 'en-IN') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'en_ISO' || goog.LOCALE == 'en-ISO') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
if (goog.LOCALE == 'en_SG' || goog.LOCALE == 'en-SG') {
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
}
-11
View File
@@ -91,15 +91,6 @@ var getTaggedVersion = function() {
return null;
};
/**
* Stable versions have an even minor version and have no prerelease
* @param {SemVer} version The version to test
* @return {Boolean} True if the version is stable
*/
var isStable = function(version) {
return semver.satisfies(version, '1.0 || 1.2') && version.prerelease.length === 0;
};
/**
* Get a collection of all the previous versions sorted by semantic version
* @return {Array.<SemVer>} The collection of previous versions
@@ -119,8 +110,6 @@ var getPreviousVersions = function() {
})
.filter()
.map(function(version) {
version.isStable = isStable(version);
version.docsUrl = 'http://code.angularjs.org/' + version.version + '/docs';
// Versions before 1.0.2 had a different docs folder name
if ( version.major < 1 || (version.major === 1 && version.minor === 0 && version.dot < 2 ) ) {
+13 -13
View File
@@ -1395,7 +1395,7 @@
}
},
"dgeni-packages": {
"version": "0.10.5",
"version": "0.10.7",
"dependencies": {
"catharsis": {
"version": "0.7.1"
@@ -1454,7 +1454,7 @@
"version": "1.2.2"
},
"estraverse": {
"version": "1.5.1"
"version": "1.7.1"
},
"glob": {
"version": "3.2.11",
@@ -1465,16 +1465,16 @@
}
},
"htmlparser2": {
"version": "3.7.3",
"version": "3.8.2",
"dependencies": {
"domhandler": {
"version": "2.2.0"
"version": "2.3.0"
},
"domutils": {
"version": "1.5.0"
},
"domelementtype": {
"version": "1.1.1"
"version": "1.1.3"
},
"readable-stream": {
"version": "1.1.13",
@@ -1528,7 +1528,7 @@
"dependencies": {
"fsevents": {
"version": "0.2.1",
"from": "fsevents@git://github.com/pipobscure/fsevents#7dcdf9fa3f8956610fd6f69f72c67bace2de7138",
"from": "git://github.com/pipobscure/fsevents#7dcdf9fa3f8956610fd6f69f72c67bace2de7138",
"resolved": "git://github.com/pipobscure/fsevents#7dcdf9fa3f8956610fd6f69f72c67bace2de7138",
"dependencies": {
"nan": {
@@ -1596,7 +1596,7 @@
"version": "0.0.10",
"dependencies": {
"combined-stream": {
"version": "0.0.5",
"version": "0.0.7",
"dependencies": {
"delayed-stream": {
"version": "0.0.5"
@@ -4810,7 +4810,7 @@
}
},
"protractor": {
"version": "1.3.1",
"version": "1.4.0",
"dependencies": {
"request": {
"version": "2.36.0",
@@ -4834,7 +4834,7 @@
"version": "0.12.1",
"dependencies": {
"punycode": {
"version": "1.3.1"
"version": "1.3.2"
}
}
},
@@ -4842,7 +4842,7 @@
"version": "0.1.4",
"dependencies": {
"combined-stream": {
"version": "0.0.5",
"version": "0.0.7",
"dependencies": {
"delayed-stream": {
"version": "0.0.5"
@@ -4897,7 +4897,7 @@
}
},
"selenium-webdriver": {
"version": "2.43.5",
"version": "2.44.0",
"dependencies": {
"tmp": {
"version": "0.0.24"
@@ -4906,7 +4906,7 @@
"version": "0.4.4",
"dependencies": {
"sax": {
"version": "0.6.0"
"version": "0.6.1"
},
"xmlbuilder": {
"version": "2.4.4",
@@ -4966,7 +4966,7 @@
"version": "1.0.0"
},
"source-map-support": {
"version": "0.2.7",
"version": "0.2.8",
"dependencies": {
"source-map": {
"version": "0.1.32",
+1 -1
View File
@@ -52,7 +52,7 @@
"marked": "~0.3.0",
"node-html-encoder": "0.0.2",
"promises-aplus-tests": "~2.1.0",
"protractor": "1.3.1",
"protractor": "1.4.0",
"q": "~1.0.0",
"q-io": "^1.10.9",
"qq": "^0.3.5",
+8 -2
View File
@@ -96,8 +96,14 @@ function publish {
# don't publish every build to npm
if [ "${NEW_VERSION/+sha}" = "$NEW_VERSION" ] ; then
if [ "${NEW_VERSION/-}" = "$NEW_VERSION" ] ; then
# publish releases as "latest"
npm publish
if [[ $NEW_VERSION =~ ^1\.2\.[0-9]+$ ]] ; then
# publish 1.2.x releases with the appropriate tag
# this ensures that `npm install` by default will not grab `1.2.x` releases
npm publish --tag=old
else
# publish releases as "latest"
npm publish
fi
else
# publish prerelease builds with the beta tag
npm publish --tag=beta
+1
View File
@@ -19,6 +19,7 @@
"angularModule": false,
"nodeName_": false,
"uid": false,
"toDebugString": false,
"REGEX_STRING_REGEXP" : false,
"lowercase": false,
+10 -10
View File
@@ -162,8 +162,8 @@ if ('i' !== 'I'.toLowerCase()) {
}
var /** holds major version number for IE or NaN for real browsers */
msie,
var
msie, // holds major version number for IE, or NaN if UA is not IE.
jqLite, // delay binding since jQuery could be loaded after us.
jQuery, // delay binding
slice = [].slice,
@@ -362,7 +362,7 @@ function int(str) {
function inherit(parent, extra) {
return extend(new (extend(function() {}, {prototype:parent}))(), extra);
return extend(Object.create(parent), extra);
}
/**
@@ -625,7 +625,7 @@ function makeMap(str) {
function nodeName_(element) {
return lowercase(element.nodeName || element[0].nodeName);
return lowercase(element.nodeName || (element[0] && element[0].nodeName));
}
function includes(array, obj) {
@@ -634,7 +634,7 @@ function includes(array, obj) {
function arrayRemove(array, value) {
var index = array.indexOf(value);
if (index >=0)
if (index >= 0)
array.splice(index, 1);
return value;
}
@@ -835,7 +835,7 @@ function equals(o1, o2) {
if (isArray(o1)) {
if (!isArray(o2)) return false;
if ((length = o1.length) == o2.length) {
for (key=0; key<length; key++) {
for (key = 0; key < length; key++) {
if (!equals(o1[key], o2[key])) return false;
}
return true;
@@ -921,7 +921,7 @@ function bind(self, fn) {
return curryArgs.length
? function() {
return arguments.length
? fn.apply(self, curryArgs.concat(slice.call(arguments, 0)))
? fn.apply(self, concat(curryArgs, arguments, 0))
: fn.apply(self, curryArgs);
}
: function() {
@@ -1121,7 +1121,7 @@ var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
function getNgAttribute(element, ngAttr) {
var attr, i, ii = ngAttrPrefixes.length;
element = jqLite(element);
for (i=0; i<ii; ++i) {
for (i = 0; i < ii; ++i) {
attr = ngAttrPrefixes[i] + ngAttr;
if (isString(attr = element.attr(attr))) {
return attr;
@@ -1331,8 +1331,8 @@ function angularInit(element, bootstrap) {
* @param {Object=} config an object for defining configuration options for the application. The
* following keys are supported:
*
* - `strictDi`: disable automatic function annotation for the application. This is meant to
* assist in finding bugs which break minified code.
* * `strictDi` - disable automatic function annotation for the application. This is meant to
* assist in finding bugs which break minified code. Defaults to `false`.
*
* @returns {auto.$injector} Returns the newly created injector for this app.
*/
+14 -10
View File
@@ -10,9 +10,10 @@
* Creates an injector object that can be used for retrieving services as well as for
* dependency injection (see {@link guide/di dependency injection}).
*
* @param {Array.<string|Function>} modules A list of module functions or their aliases. See
* {@link angular.module}. The `ng` module must be explicitly added.
* {@link angular.module}. The `ng` module must be explicitly added.
* @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
* disallows argument name annotation inference.
* @returns {injector} Injector object. See {@link auto.$injector $injector}.
*
* @example
@@ -158,8 +159,10 @@ function annotate(fn, strictDi, name) {
* ## Inference
*
* In JavaScript calling `toString()` on a function returns the function definition. The definition
* can then be parsed and the function arguments can be extracted. *NOTE:* This does not work with
* minification, and obfuscation tools since these tools change the argument names.
* can then be parsed and the function arguments can be extracted. This method of discovering
* annotations is disallowed when the injector is in strict mode.
* *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
* argument names.
*
* ## `$inject` Annotation
* By adding an `$inject` property onto a function the injection parameters can be specified.
@@ -244,6 +247,8 @@ function annotate(fn, strictDi, name) {
* expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
* ```
*
* You can disallow this method by using strict injection mode.
*
* This method does not work with code minification / obfuscation. For this reason the following
* annotation strategies are supported.
*
@@ -296,6 +301,8 @@ function annotate(fn, strictDi, name) {
* @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
* be retrieved as described above.
*
* @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
*
* @returns {Array.<string>} The names of the services which the function requires.
*/
@@ -815,14 +822,11 @@ function createInjector(modulesToLoad, strictDi) {
}
function instantiate(Type, locals, serviceName) {
var Constructor = function() {},
instance, returnedValue;
// Check if Type is annotated and use just the given function at n-1 as parameter
// e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype;
instance = new Constructor();
returnedValue = invoke(Type, instance, locals, serviceName);
// Object creation: http://jsperf.com/create-constructor/2
var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype);
var returnedValue = invoke(Type, instance, locals, serviceName);
return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
}
+4 -2
View File
@@ -87,10 +87,12 @@
* `'ngModel'`).
* - `injector()` - retrieves the injector of the current element or its parent.
* - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
* element or its parent.
* element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
* be enabled.
* - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
* current element. This getter should be used only on elements that contain a directive which starts a new isolate
* scope. Calling `scope()` on this element always returns the original non-isolate scope.
* Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
* - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
* parent element is reached.
*
@@ -597,7 +599,7 @@ forEach({
}
} else {
return (element[name] ||
(element.attributes.getNamedItem(name)|| noop).specified)
(element.attributes.getNamedItem(name) || noop).specified)
? lowercasedName
: undefined;
}
+4 -21
View File
@@ -37,31 +37,14 @@ function minErr(module, ErrorConstructor) {
prefix = '[' + (module ? module + ':' : '') + code + '] ',
template = arguments[1],
templateArgs = arguments,
stringify = function(obj) {
if (typeof obj === 'function') {
return obj.toString().replace(/ \{[\s\S]*$/, '');
} else if (typeof obj === 'undefined') {
return 'undefined';
} else if (typeof obj !== 'string') {
return JSON.stringify(obj);
}
return obj;
},
message, i;
message = prefix + template.replace(/\{\d+\}/g, function(match) {
var index = +match.slice(1, -1), arg;
if (index + 2 < templateArgs.length) {
arg = templateArgs[index + 2];
if (typeof arg === 'function') {
return arg.toString().replace(/ ?\{[\s\S]*$/, '');
} else if (typeof arg === 'undefined') {
return 'undefined';
} else if (typeof arg !== 'string') {
return toJson(arg);
}
return arg;
return toDebugString(templateArgs[index + 2]);
}
return match;
});
@@ -69,8 +52,8 @@ function minErr(module, ErrorConstructor) {
message = message + '\nhttp://errors.angularjs.org/"NG_VERSION_FULL"/' +
(module ? module + '/' : '') + code;
for (i = 2; i < arguments.length; i++) {
message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
encodeURIComponent(stringify(arguments[i]));
message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
encodeURIComponent(toDebugString(arguments[i]));
}
return new ErrorConstructor(message);
};
+1 -1
View File
@@ -17,7 +17,7 @@ function $AnchorScrollProvider() {
* @name $anchorScrollProvider#disableAutoScrolling
*
* @description
* By default, {@link ng.$anchorScroll $anchorScroll()} will automatically will detect changes to
* By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
* {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
* Use this method to disable automatic scrolling.
*
+4 -5
View File
@@ -19,8 +19,7 @@
/**
* @param {object} window The global window object.
* @param {object} document jQuery wrapped document.
* @param {function()} XHR XMLHttpRequest constructor.
* @param {object} $log console.log or an object with the same interface.
* @param {object} $log window.console or an object with the same interface.
* @param {object} $sniffer $sniffer service
*/
function Browser(window, document, $log, $sniffer) {
@@ -171,7 +170,7 @@ function Browser(window, document, $log, $sniffer) {
// IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
// See https://github.com/angular/angular.js/commit/ffb2701
if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
return;
return self;
}
var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
lastBrowserUrl = url;
@@ -370,8 +369,8 @@ function Browser(window, document, $log, $sniffer) {
// - 20 cookies per unique domain
// - 4096 bytes per cookie
if (cookieLength > 4096) {
$log.warn("Cookie '"+ name +
"' possibly not set or overflowed because it was too large ("+
$log.warn("Cookie '" + name +
"' possibly not set or overflowed because it was too large (" +
cookieLength + " > 4096 bytes)!");
}
}
+64 -20
View File
@@ -180,7 +180,7 @@
*
*
* #### `bindToController`
* When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController` will
* When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
* allow a component to have its properties bound to the controller, rather than to scope. When the controller
* is instantiated, the initial values of the isolate scope bindings are already available.
*
@@ -622,10 +622,17 @@
*
*
* @param {string|DOMElement} element Element or HTML string to compile into a template function.
* @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives.
* @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
*
* <div class="alert alert-error">
* **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
* e.g. will not use the right outer scope. Please pass the transclude function as a
* `parentBoundTranscludeFn` to the link function instead.
* </div>
*
* @param {number} maxPriority only apply directives lower than given priority (Only effects the
* root element(s), not their children)
* @returns {function(scope, cloneAttachFn=)} a link function which is used to bind template
* @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
* (a DOM element/tree) to a scope. Where:
*
* * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
@@ -637,6 +644,19 @@
* * `clonedElement` - is a clone of the original `element` passed into the compiler.
* * `scope` - is the current scope with which the linking function is working with.
*
* * `options` - An optional object hash with linking options. If `options` is provided, then the following
* keys may be used to control linking behavior:
*
* * `parentBoundTranscludeFn` - the transclude function made available to
* directives; if given, it will be passed through to the link functions of
* directives found in `element` during compilation.
* * `transcludeControllers` - an object hash with keys that map controller names
* to controller instances; if given, it will make the controllers
* available to directives.
* * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
* the cloned elements; only needed for transcludes that are allowed to contain non html
* elements (e.g. SVG elements). See also the directive.controller property.
*
* Calling the linking function returns the element of the template. It is either the original
* element passed in, or the clone of the element if the `cloneAttachFn` is provided.
*
@@ -783,7 +803,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* Retrieves or overrides the default regular expression that is used for whitelisting of safe
* urls during a[href] sanitization.
*
* The sanitization is a security measure aimed at prevent XSS attacks via html links.
* The sanitization is a security measure aimed at preventing XSS attacks via html links.
*
* Any url about to be assigned to a[href] via data-binding is first normalized and turned into
* an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
@@ -850,7 +870,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* * `ng-binding` CSS class
* * `$binding` data property containing an array of the binding expressions
*
* You may want to use this in production for a significant performance boost. See
* You may want to disable this in production for a significant performance boost. See
* {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
*
* The default value is true.
@@ -1009,16 +1029,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// for each tuples
var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
for (var i=0; i<nbrUrisWith2parts; i++) {
var innerIdx = i*2;
for (var i = 0; i < nbrUrisWith2parts; i++) {
var innerIdx = i * 2;
// sanitize the uri
result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
// add the descriptor
result += (" " + trim(rawUris[innerIdx+1]));
result += (" " + trim(rawUris[innerIdx + 1]));
}
// split the last item into uri and descriptor
var lastTuple = trim(rawUris[i*2]).split(/\s/);
var lastTuple = trim(rawUris[i * 2]).split(/\s/);
// sanitize the last uri
result += $$sanitizeUri(trim(lastTuple[0]), true);
@@ -1155,8 +1175,22 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
maxPriority, ignoreDirective, previousCompileContext);
compile.$$addScopeClass($compileNodes);
var namespace = null;
return function publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn, futureParentElement) {
return function publicLinkFn(scope, cloneConnectFn, options) {
assertArg(scope, 'scope');
options = options || {};
var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
transcludeControllers = options.transcludeControllers,
futureParentElement = options.futureParentElement;
// When `parentBoundTranscludeFn` is passed, it is a
// `controllersBoundTransclude` function (it was previously passed
// as `transclude` to directive.link) so we must unwrap it to get
// its `boundTranscludeFn`
if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
}
if (!namespace) {
namespace = detectNamespaceForChildElements(futureParentElement);
}
@@ -1198,7 +1232,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (!node) {
return 'html';
} else {
return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg': 'html';
return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
}
}
@@ -1326,7 +1360,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
transcludedScope.$$transcluded = true;
}
return transcludeFn(transcludedScope, cloneFn, controllers, previousBoundTranscludeFn, futureParentElement);
return transcludeFn(transcludedScope, cloneFn, {
parentBoundTranscludeFn: previousBoundTranscludeFn,
transcludeControllers: controllers,
futureParentElement: futureParentElement
});
};
return boundTranscludeFn;
@@ -1794,7 +1832,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
isolateScope = scope.$new(true);
}
transcludeFn = boundTranscludeFn && controllersBoundTransclude;
if (boundTranscludeFn) {
// track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
// is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
transcludeFn = controllersBoundTransclude;
transcludeFn.$$boundTransclude = boundTranscludeFn;
}
if (controllerDirectives) {
// TODO: merge `controllers` and `elementControllers` into single object.
controllers = {};
@@ -2001,7 +2045,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var match = null;
if (hasDirectives.hasOwnProperty(name)) {
for (var directive, directives = $injector.get(name + Suffix),
i = 0, ii = directives.length; i<ii; i++) {
i = 0, ii = directives.length; i < ii; i++) {
try {
directive = directives[i];
if ((maxPriority === undefined || maxPriority > directive.priority) &&
@@ -2030,7 +2074,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
function directiveIsMultiElement(name) {
if (hasDirectives.hasOwnProperty(name)) {
for (var directive, directives = $injector.get(name + Suffix),
i = 0, ii = directives.length; i<ii; i++) {
i = 0, ii = directives.length; i < ii; i++) {
directive = directives[i];
if (directive.multiElement) {
return true;
@@ -2183,10 +2227,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var childBoundTranscludeFn = boundTranscludeFn;
if (scope.$$destroyed) return;
if (linkQueue) {
linkQueue.push(scope);
linkQueue.push(node);
linkQueue.push(rootElement);
linkQueue.push(childBoundTranscludeFn);
linkQueue.push(scope,
node,
rootElement,
childBoundTranscludeFn);
} else {
if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
@@ -2249,7 +2293,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
case 'svg':
case 'math':
var wrapper = document.createElement('div');
wrapper.innerHTML = '<'+type+'>'+template+'</'+type+'>';
wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
return wrapper.childNodes[0].childNodes;
default:
return template;
+7 -3
View File
@@ -59,6 +59,10 @@ function $ControllerProvider() {
* * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
* `window` object (not recommended)
*
* The string can use the `controller as property` syntax, where the controller instance is published
* as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
* to work correctly.
*
* @param {Object} locals Injection locals for Controller.
* @return {Object} Instance of given controller.
*
@@ -104,10 +108,10 @@ function $ControllerProvider() {
//
// This feature is not intended for use by applications, and is thus not documented
// publicly.
var Constructor = function() {};
Constructor.prototype = (isArray(expression) ?
// Object creation: http://jsperf.com/create-constructor/2
var controllerPrototype = (isArray(expression) ?
expression[expression.length - 1] : expression).prototype;
instance = new Constructor();
instance = Object.create(controllerPrototype);
if (identifier) {
addIdentifier(locals, identifier, instance, constructor || expression.name);
+2 -3
View File
@@ -11,9 +11,8 @@
* make the link go to the wrong URL if the user clicks it before
* Angular has a chance to replace the `{{hash}}` markup with its
* value. Until Angular replaces the markup the link will be broken
* and will most likely return a 404 error.
*
* The `ngHref` directive solves this problem.
* and will most likely return a 404 error. The `ngHref` directive
* solves this problem.
*
* The wrong way to write it:
* ```html
+1 -3
View File
@@ -471,9 +471,7 @@ var formDirectiveFactory = function(isNgForm) {
controller.$setSubmitted();
});
event.preventDefault
? event.preventDefault()
: event.returnValue = false; // IE
event.preventDefault();
};
addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
+215 -114
View File
@@ -41,10 +41,16 @@ var inputType = {
* @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
* minlength.
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
* maxlength.
* @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
* RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
* patterns defined as scope expressions.
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
* any length.
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
* a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object then this is used directly.
* If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
* characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
* @param {string=} ngChange Angular expression to be executed when input changes due to user
* interaction with the input element.
* @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
@@ -200,7 +206,7 @@ var inputType = {
/**
* @ngdoc input
* @name input[dateTimeLocal]
* @name input[datetime-local]
*
* @description
* Input with datetime validation and transformation. In browsers that do not yet support
@@ -584,10 +590,16 @@ var inputType = {
* @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
* minlength.
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
* maxlength.
* @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
* RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
* patterns defined as scope expressions.
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
* any length.
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
* a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object then this is used directly.
* If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
* characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
* @param {string=} ngChange Angular expression to be executed when input changes due to user
* interaction with the input element.
*
@@ -666,10 +678,16 @@ var inputType = {
* @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
* minlength.
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
* maxlength.
* @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
* RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
* patterns defined as scope expressions.
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
* any length.
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
* a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object then this is used directly.
* If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
* characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
* @param {string=} ngChange Angular expression to be executed when input changes due to user
* interaction with the input element.
*
@@ -749,10 +767,16 @@ var inputType = {
* @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
* minlength.
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
* maxlength.
* @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
* RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
* patterns defined as scope expressions.
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
* any length.
* @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
* that contains the regular expression body that will be converted to a regular expression
* as in the ngPattern directive.
* @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
* a RegExp found by evaluating the Angular expression given in the attribute value.
* If the expression evaluates to a RegExp object then this is used directly.
* If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
* characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
* @param {string=} ngChange Angular expression to be executed when input changes due to user
* interaction with the input element.
*
@@ -1015,7 +1039,7 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
element.on('change', listener);
ctrl.$render = function() {
element.val(ctrl.$isEmpty(ctrl.$modelValue) ? '' : ctrl.$viewValue);
element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
};
}
@@ -1063,8 +1087,8 @@ function createDateParser(regexp, mapping) {
// When a date is JSON'ified to wraps itself inside of an extra
// set of double quotes. This makes the date parsing code unable
// to match the date string and parse it as a date.
if (iso.charAt(0) == '"' && iso.charAt(iso.length-1) == '"') {
iso = iso.substring(1, iso.length-1);
if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
iso = iso.substring(1, iso.length - 1);
}
if (ISO_DATE_REGEXP.test(iso)) {
return new Date(iso);
@@ -1125,10 +1149,10 @@ function createDateInputType(type, regexp, parseDate, format) {
});
ctrl.$formatters.push(function(value) {
if (!ctrl.$isEmpty(value)) {
if (!isDate(value)) {
throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
}
if (value && !isDate(value)) {
throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
}
if (isValidDate(value)) {
previousDate = value;
if (previousDate && timezone === 'UTC') {
var timezoneOffset = 60000 * previousDate.getTimezoneOffset();
@@ -1137,14 +1161,14 @@ function createDateInputType(type, regexp, parseDate, format) {
return $filter('date')(value, format, timezone);
} else {
previousDate = null;
return '';
}
return '';
});
if (isDefined(attr.min) || attr.ngMin) {
var minVal;
ctrl.$validators.min = function(value) {
return ctrl.$isEmpty(value) || isUndefined(minVal) || parseDate(value) >= minVal;
return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
};
attr.$observe('min', function(val) {
minVal = parseObservedDateValue(val);
@@ -1155,18 +1179,18 @@ function createDateInputType(type, regexp, parseDate, format) {
if (isDefined(attr.max) || attr.ngMax) {
var maxVal;
ctrl.$validators.max = function(value) {
return ctrl.$isEmpty(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
};
attr.$observe('max', function(val) {
maxVal = parseObservedDateValue(val);
ctrl.$validate();
});
}
// Override the standard $isEmpty to detect invalid dates as well
ctrl.$isEmpty = function(value) {
function isValidDate(value) {
// Invalid Date: getTime() returns NaN
return !value || (value.getTime && value.getTime() !== value.getTime());
};
return value && !(value.getTime && value.getTime() !== value.getTime());
}
function parseObservedDateValue(val) {
return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
@@ -1250,7 +1274,8 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
stringBasedInputType(ctrl);
ctrl.$$parserName = 'url';
ctrl.$validators.url = function(value) {
ctrl.$validators.url = function(modelValue, viewValue) {
var value = modelValue || viewValue;
return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
};
}
@@ -1262,7 +1287,8 @@ function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
stringBasedInputType(ctrl);
ctrl.$$parserName = 'email';
ctrl.$validators.email = function(value) {
ctrl.$validators.email = function(modelValue, viewValue) {
var value = modelValue || viewValue;
return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
};
}
@@ -1316,9 +1342,11 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
element[0].checked = ctrl.$viewValue;
};
// Override the standard `$isEmpty` because an empty checkbox is never equal to the trueValue
// Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
// This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
// it to a boolean.
ctrl.$isEmpty = function(value) {
return value !== trueValue;
return value === false;
};
ctrl.$formatters.push(function(value) {
@@ -1350,7 +1378,8 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
* @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
* minlength.
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
* maxlength.
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
* length.
* @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
* RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
* patterns defined as scope expressions.
@@ -1382,7 +1411,8 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
* @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
* minlength.
* @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
* maxlength.
* maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
* length.
* @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
* RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
* patterns defined as scope expressions.
@@ -1511,12 +1541,17 @@ var VALID_CLASS = 'ng-valid',
* @property {string} $viewValue Actual string value in the view.
* @property {*} $modelValue The value in the model that the control is bound to.
* @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
the control reads value from the DOM. The functions are called in array order, each passing the value
through to the next. The last return value is forwarded to the $validators collection.
Used to sanitize / convert the value.
Returning undefined from a parser means a parse error occurred. No $validators will
run and the 'ngModel' will not be updated until the parse error is resolved. The parse error is stored
in 'ngModel.$error.parse'.
the control reads value from the DOM. The functions are called in array order, each passing
its return value through to the next. The last return value is forwarded to the
{@link ngModel.NgModelController#$validators `$validators`} collection.
Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
`$viewValue`}.
Returning `undefined` from a parser means a parse error occurred. In that case,
no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
is set to `true`. The parse error is stored in `ngModel.$error.parse`.
*
* @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
@@ -1576,9 +1611,6 @@ var VALID_CLASS = 'ng-valid',
* };
* ```
*
* @param {string} name The name of the validator.
* @param {Function} validationFn The validation function that will be run.
*
* @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
* view value has changed. It is called with no arguments, and its return value is ignored.
* This can be used in place of additional $watches against the model value.
@@ -1592,16 +1624,22 @@ var VALID_CLASS = 'ng-valid',
* @property {boolean} $dirty True if user has already interacted with the control.
* @property {boolean} $valid True if there is no error.
* @property {boolean} $invalid True if at least one error on the control.
* @property {string} $name The name attribute of the control.
*
* @description
*
* `NgModelController` provides API for the `ng-model` directive. The controller contains
* services for data-binding, validation, CSS updates, and value formatting and parsing. It
* purposefully does not contain any logic which deals with DOM rendering or listening to
* DOM events. Such DOM related logic should be provided by other directives which make use of
* `NgModelController` for data-binding.
* `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
* The controller contains services for data-binding, validation, CSS updates, and value formatting
* and parsing. It purposefully does not contain any logic which deals with DOM rendering or
* listening to DOM events.
* Such DOM related logic should be provided by other directives which make use of
* `NgModelController` for data-binding to control elements.
* Angular provides this DOM logic for most {@link input `input`} elements.
* At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
* custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
*
* ## Custom Control Example
* @example
* ### Custom Control Example
* This example shows how to use `NgModelController` with a custom control to achieve
* data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
* collaborate together to achieve the desired result.
@@ -1643,7 +1681,7 @@ var VALID_CLASS = 'ng-valid',
// Listen for change events to enable binding
element.on('blur keyup change', function() {
scope.$apply(read);
scope.$evalAsync(read);
});
read(); // initialize
@@ -1698,6 +1736,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
this.$viewValue = Number.NaN;
this.$modelValue = Number.NaN;
this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
this.$validators = {};
this.$asyncValidators = {};
this.$parsers = [];
@@ -1716,32 +1755,33 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
var parsedNgModel = $parse($attr.ngModel),
parsedNgModelAssign = parsedNgModel.assign,
ngModelGet = parsedNgModel,
ngModelSet = parsedNgModelAssign,
pendingDebounce = null,
ctrl = this;
var ngModelGet = function ngModelGet() {
var modelValue = parsedNgModel($scope);
if (ctrl.$options && ctrl.$options.getterSetter && isFunction(modelValue)) {
modelValue = modelValue();
}
return modelValue;
};
var ngModelSet = function ngModelSet(newValue) {
var getterSetter;
if (ctrl.$options && ctrl.$options.getterSetter &&
isFunction(getterSetter = parsedNgModel($scope))) {
getterSetter(ctrl.$modelValue);
} else {
parsedNgModel.assign($scope, ctrl.$modelValue);
}
};
this.$$setOptions = function(options) {
ctrl.$options = options;
if (options && options.getterSetter) {
var invokeModelGetter = $parse($attr.ngModel + '()'),
invokeModelSetter = $parse($attr.ngModel + '($$$p)');
if (!parsedNgModel.assign && (!options || !options.getterSetter)) {
ngModelGet = function($scope) {
var modelValue = parsedNgModel($scope);
if (isFunction(modelValue)) {
modelValue = invokeModelGetter($scope);
}
return modelValue;
};
ngModelSet = function($scope, newValue) {
if (isFunction(parsedNgModel($scope))) {
invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
} else {
parsedNgModelAssign($scope, ctrl.$modelValue);
}
};
} else if (!parsedNgModel.assign) {
throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
$attr.ngModel, startingTag($element));
}
@@ -1774,17 +1814,18 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* @name ngModel.NgModelController#$isEmpty
*
* @description
* This is called when we need to determine if the value of the input is empty.
* This is called when we need to determine if the value of an input is empty.
*
* For instance, the required directive does this to work out if the input has data or not.
*
* The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
*
* You can override this for input directives whose concept of being empty is different to the
* default. The `checkboxInputType` directive does this because in its case a value of `false`
* implies empty.
*
* @param {*} value Model value to check.
* @returns {boolean} True if `value` is empty.
* @param {*} value The value of the input to check for emptiness.
* @returns {boolean} True if `value` is "empty".
*/
this.$isEmpty = function(value) {
return isUndefined(value) || value === '' || value === null || value !== value;
@@ -1835,9 +1876,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* @description
* Sets the control to its pristine state.
*
* This method can be called to remove the 'ng-dirty' class and set the control to its pristine
* state (ng-pristine class). A model is considered to be pristine when the model has not been changed
* from when first compiled within then form.
* This method can be called to remove the `ng-dirty` class and set the control to its pristine
* state (`ng-pristine` class). A model is considered to be pristine when the control
* has not been changed from when first compiled.
*/
this.$setPristine = function() {
ctrl.$dirty = false;
@@ -1846,6 +1887,25 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
$animate.addClass($element, PRISTINE_CLASS);
};
/**
* @ngdoc method
* @name ngModel.NgModelController#$setDirty
*
* @description
* Sets the control to its dirty state.
*
* This method can be called to remove the `ng-pristine` class and set the control to its dirty
* state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
* from when first compiled.
*/
this.$setDirty = function() {
ctrl.$dirty = true;
ctrl.$pristine = false;
$animate.removeClass($element, PRISTINE_CLASS);
$animate.addClass($element, DIRTY_CLASS);
parentForm.$setDirty();
};
/**
* @ngdoc method
* @name ngModel.NgModelController#$setUntouched
@@ -1853,8 +1913,8 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* @description
* Sets the control to its untouched state.
*
* This method can be called to remove the 'ng-touched' class and set the control to its
* untouched state (ng-untouched class). Upon compilation, a model is set as untouched
* This method can be called to remove the `ng-touched` class and set the control to its
* untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
* by default, however this function can be used to restore that state if the model has
* already been touched by the user.
*/
@@ -1871,10 +1931,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* @description
* Sets the control to its touched state.
*
* This method can be called to remove the 'ng-untouched' class and set the control to its
* touched state (ng-touched class). A model is considered to be touched when the user has
* first interacted (focussed) on the model input element and then shifted focus away (blurred)
* from the input element.
* This method can be called to remove the `ng-untouched` class and set the control to its
* touched state (`ng-touched` class). A model is considered to be touched when the user has
* first focused the control element and then shifted focus away from the control (blur event).
*/
this.$setTouched = function() {
ctrl.$touched = true;
@@ -1952,14 +2011,51 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* @name ngModel.NgModelController#$validate
*
* @description
* Runs each of the registered validators (first synchronous validators and then asynchronous validators).
* Runs each of the registered validators (first synchronous validators and then
* asynchronous validators).
* If the validity changes to invalid, the model will be set to `undefined`,
* unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
* If the validity changes to valid, it will set the model to the last available valid
* modelValue, i.e. either the last parsed value or the last value set from the scope.
*/
this.$validate = function() {
// ignore $validate before model is initialized
if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
return;
}
this.$$parseAndValidate();
var viewValue = ctrl.$$lastCommittedViewValue;
// Note: we use the $$rawModelValue as $modelValue might have been
// set to undefined during a view -> model update that found validation
// errors. We can't parse the view here, since that could change
// the model although neither viewValue nor the model on the scope changed
var modelValue = ctrl.$$rawModelValue;
// Check if the there's a parse error, so we don't unset it accidentially
var parserName = ctrl.$$parserName || 'parse';
var parserValid = ctrl.$error[parserName] ? false : undefined;
var prevValid = ctrl.$valid;
var prevModelValue = ctrl.$modelValue;
var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
ctrl.$$runValidators(parserValid, modelValue, viewValue, function(allValid) {
// If there was no change in validity, don't update the model
// This prevents changing an invalid modelValue to undefined
if (!allowInvalid && prevValid !== allValid) {
// Note: Don't check ctrl.$valid here, as we could have
// external validators (e.g. calculated on the server),
// that just call $setValidity and need the model value
// to calculate their validity.
ctrl.$modelValue = allValid ? modelValue : undefined;
if (ctrl.$modelValue !== prevModelValue) {
ctrl.$$writeModelToScope();
}
}
});
};
this.$$runValidators = function(parseValid, modelValue, viewValue, doneCallback) {
@@ -2078,11 +2174,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
// change to dirty
if (ctrl.$pristine) {
ctrl.$dirty = true;
ctrl.$pristine = false;
$animate.removeClass($element, PRISTINE_CLASS);
$animate.addClass($element, DIRTY_CLASS);
parentForm.$setDirty();
this.$setDirty();
}
this.$$parseAndValidate();
};
@@ -2103,10 +2195,11 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
}
if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
// ctrl.$modelValue has not been touched yet...
ctrl.$modelValue = ngModelGet();
ctrl.$modelValue = ngModelGet($scope);
}
var prevModelValue = ctrl.$modelValue;
var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
ctrl.$$rawModelValue = modelValue;
if (allowInvalid) {
ctrl.$modelValue = modelValue;
writeToModelIfNeeded();
@@ -2130,7 +2223,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
};
this.$$writeModelToScope = function() {
ngModelSet(ctrl.$modelValue);
ngModelSet($scope, ctrl.$modelValue);
forEach(ctrl.$viewChangeListeners, function(listener) {
try {
listener();
@@ -2226,12 +2319,12 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
// ng-change executes in apply phase
// 4. view should be changed back to 'a'
$scope.$watch(function ngModelWatch() {
var modelValue = ngModelGet();
var modelValue = ngModelGet($scope);
// if scope model value and ngModel value are out of sync
// TODO(perf): why not move this to the action fn?
if (modelValue !== ctrl.$modelValue) {
ctrl.$modelValue = modelValue;
ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
var formatters = ctrl.$formatters,
idx = formatters.length;
@@ -2292,7 +2385,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* - {@link input[email] email}
* - {@link input[url] url}
* - {@link input[date] date}
* - {@link input[dateTimeLocal] dateTimeLocal}
* - {@link input[datetime-local] datetime-local}
* - {@link input[time] time}
* - {@link input[month] month}
* - {@link input[week] week}
@@ -2416,7 +2509,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
</file>
* </example>
*/
var ngModelDirective = function() {
var ngModelDirective = ['$rootScope', function($rootScope) {
return {
restrict: 'A',
require: ['ngModel', '^?form', '^?ngModelOptions'],
@@ -2460,15 +2553,17 @@ var ngModelDirective = function() {
element.on('blur', function(ev) {
if (modelCtrl.$touched) return;
scope.$apply(function() {
modelCtrl.$setTouched();
});
if ($rootScope.$$phase) {
scope.$evalAsync(modelCtrl.$setTouched);
} else {
scope.$apply(modelCtrl.$setTouched);
}
});
}
};
}
};
};
}];
/**
@@ -2557,8 +2652,8 @@ var requiredDirective = function() {
if (!ctrl) return;
attr.required = true; // force truthy in case we are on non input element
ctrl.$validators.required = function(value) {
return !attr.required || !ctrl.$isEmpty(value);
ctrl.$validators.required = function(modelValue, viewValue) {
return !attr.required || !ctrl.$isEmpty(viewValue);
};
attr.$observe('required', function() {
@@ -2579,7 +2674,7 @@ var patternDirective = function() {
var regexp, patternExp = attr.ngPattern || attr.pattern;
attr.$observe('pattern', function(regex) {
if (isString(regex) && regex.length > 0) {
regex = new RegExp(regex);
regex = new RegExp('^' + regex + '$');
}
if (regex && !regex.test) {
@@ -2607,13 +2702,14 @@ var maxlengthDirective = function() {
link: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
var maxlength = 0;
var maxlength = -1;
attr.$observe('maxlength', function(value) {
maxlength = int(value) || 0;
var intVal = int(value);
maxlength = isNaN(intVal) ? -1 : intVal;
ctrl.$validate();
});
ctrl.$validators.maxlength = function(modelValue, viewValue) {
return ctrl.$isEmpty(modelValue) || viewValue.length <= maxlength;
return (maxlength < 0) || ctrl.$isEmpty(modelValue) || (viewValue.length <= maxlength);
};
}
};
@@ -2632,7 +2728,7 @@ var minlengthDirective = function() {
ctrl.$validate();
});
ctrl.$validators.minlength = function(modelValue, viewValue) {
return ctrl.$isEmpty(modelValue) || viewValue.length >= minlength;
return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
};
}
};
@@ -2772,12 +2868,17 @@ var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
* @name ngValue
*
* @description
* Binds the given expression to the value of `option` or `input[radio]`, so
* that when the element is selected, the `ngModel` of that element is set to
* Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
* so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
* the bound value.
*
* `ngValue` is useful when dynamically generating lists of radio buttons using `ng-repeat`, as
* shown below.
* `ngValue` is useful when dynamically generating lists of radio buttons using
* {@link ngRepeat `ngRepeat`}, as shown below.
*
* Likewise, `ngValue` can be used to generate `<option>` elements for
* the {@link select `select`} element. In that case however, only strings are supported
* for the `value `attribute, so the resulting `ngModel` will always be a string.
* Support for `select` models with non-string values is available via `ngOptions`.
*
* @element input
* @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
@@ -2865,7 +2966,7 @@ var ngValueDirective = function() {
* `ngModelOptions` has an effect on the element it's declared on and its descendants.
*
* @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
* - `updateOn`: string specifying which event should be the input bound to. You can set several
* - `updateOn`: string specifying which event should the input be bound to. You can set several
* events using an space delimited list. There is a special event called `default` that
* matches the default events belonging of the control.
* - `debounce`: integer value which contains the debounce model update value in milliseconds. A
+5 -6
View File
@@ -141,12 +141,11 @@ var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate
* @name ngBindHtml
*
* @description
* Creates a binding that will innerHTML the result of evaluating the `expression` into the current
* element in a secure way. By default, the innerHTML-ed content will be sanitized using the {@link
* ngSanitize.$sanitize $sanitize} service. To utilize this functionality, ensure that `$sanitize`
* is available, for example, by including {@link ngSanitize} in your module's dependencies (not in
* core Angular). In order to use {@link ngSanitize} in your module's dependencies, you need to
* include "angular-sanitize.js" in your application.
* Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
* the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
* To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
* ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
* in your module's dependencies, you need to include "angular-sanitize.js" in your application.
*
* You may also bypass sanitization for values you know are safe. To do so, bind to
* an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
+5 -1
View File
@@ -53,7 +53,11 @@ forEach(
return {
restrict: 'A',
compile: function($element, attr) {
var fn = $parse(attr[directiveName]);
// We expose the powerful $event object on the scope that provides access to the Window,
// etc. that isn't protected by the fast paths in $parse. We explicitly request better
// checks at the cost of speed since event handler expressions are not executed as
// frequently as regular change detection.
var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
return function ngEventHandler(scope, element) {
element.on(eventName, function(event) {
var callback = function() {
+1 -1
View File
@@ -284,7 +284,7 @@ var ngIncludeFillContentDirective = ['$compile',
$compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
function namespaceAdaptedClone(clone) {
$element.append(clone);
}, undefined, undefined, $element);
}, {futureParentElement: $element});
return;
}
+31 -19
View File
@@ -173,7 +173,9 @@
</example>
*/
var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
var BRACE = /{}/g;
var BRACE = /{}/g,
IS_WHEN = /^when(Minus)?(.+)$/;
return {
restrict: 'EA',
link: function(scope, element, attr) {
@@ -184,34 +186,44 @@ var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interp
whensExpFns = {},
startSymbol = $interpolate.startSymbol(),
endSymbol = $interpolate.endSymbol(),
isWhen = /^when(Minus)?(.+)$/;
braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
watchRemover = angular.noop,
lastCount;
forEach(attr, function(expression, attributeName) {
if (isWhen.test(attributeName)) {
whens[lowercase(attributeName.replace('when', '').replace('Minus', '-'))] =
element.attr(attr.$attr[attributeName]);
var tmpMatch = IS_WHEN.exec(attributeName);
if (tmpMatch) {
var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
whens[whenKey] = element.attr(attr.$attr[attributeName]);
}
});
forEach(whens, function(expression, key) {
whensExpFns[key] =
$interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' +
offset + endSymbol));
whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
});
scope.$watch(function ngPluralizeWatch() {
var value = parseFloat(scope.$eval(numberExp));
scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
var count = parseFloat(newVal);
var countIsNaN = isNaN(count);
if (!isNaN(value)) {
//if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise,
//check it against pluralization rules in $locale service
if (!(value in whens)) value = $locale.pluralCat(value - offset);
return whensExpFns[value](scope);
} else {
return '';
if (!countIsNaN && !(count in whens)) {
// If an explicit number rule such as 1, 2, 3... is defined, just use it.
// Otherwise, check it against pluralization rules in $locale service.
count = $locale.pluralCat(count - offset);
}
// If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
// In JS `NaN !== NaN`, so we have to exlicitly check.
if ((count !== lastCount) && !(countIsNaN && isNaN(lastCount))) {
watchRemover();
watchRemover = scope.$watch(whensExpFns[count], updateElementText);
lastCount = count;
}
}, function ngPluralizeWatchAction(newVal) {
element.text(newVal);
});
function updateElementText(newText) {
element.text(newText || '');
}
}
};
}];
+1 -1
View File
@@ -366,7 +366,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
});
throw ngRepeatMinErr('dupes',
"Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
expression, trackById, toJson(value));
expression, trackById, value);
} else {
// new never before seen block
nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
+39 -39
View File
@@ -38,17 +38,17 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
*
* ### Overriding `.ng-hide`
*
* By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change
* By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
* the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
* class in CSS:
*
* ```css
* .ng-hide {
* /&#42; this is just another form of hiding an element &#42;/
* display:block!important;
* position:absolute;
* top:-9999px;
* left:-9999px;
* display: block!important;
* position: absolute;
* top: -9999px;
* left: -9999px;
* }
* ```
*
@@ -68,13 +68,13 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* .my-element.ng-hide-add, .my-element.ng-hide-remove {
* /&#42; this is required as of 1.3x to properly
* apply all styling in a show/hide animation &#42;/
* transition:0s linear all;
* transition: 0s linear all;
* }
*
* .my-element.ng-hide-add-active,
* .my-element.ng-hide-remove-active {
* /&#42; the transition is defined in the active class &#42;/
* transition:1s linear all;
* transition: 1s linear all;
* }
*
* .my-element.ng-hide-add { ... }
@@ -116,29 +116,29 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
</file>
<file name="animations.css">
.animate-show {
line-height:20px;
opacity:1;
padding:10px;
border:1px solid black;
background:white;
line-height: 20px;
opacity: 1;
padding: 10px;
border: 1px solid black;
background: white;
}
.animate-show.ng-hide-add.ng-hide-add-active,
.animate-show.ng-hide-remove.ng-hide-remove-active {
-webkit-transition:all linear 0.5s;
transition:all linear 0.5s;
-webkit-transition: all linear 0.5s;
transition: all linear 0.5s;
}
.animate-show.ng-hide {
line-height:0;
opacity:0;
padding:0 10px;
line-height: 0;
opacity: 0;
padding: 0 10px;
}
.check-element {
padding:10px;
border:1px solid black;
background:white;
padding: 10px;
border: 1px solid black;
background: white;
}
</file>
<file name="protractor.js" type="protractor">
@@ -212,17 +212,17 @@ var ngShowDirective = ['$animate', function($animate) {
*
* ### Overriding `.ng-hide`
*
* By default, the `.ng-hide` class will style the element with `display:none!important`. If you wish to change
* By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
* the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
* class in CSS:
*
* ```css
* .ng-hide {
* /&#42; this is just another form of hiding an element &#42;/
* display:block!important;
* position:absolute;
* top:-9999px;
* left:-9999px;
* display: block!important;
* position: absolute;
* top: -9999px;
* left: -9999px;
* }
* ```
*
@@ -239,7 +239,7 @@ var ngShowDirective = ['$animate', function($animate) {
* //a working example can be found at the bottom of this page
* //
* .my-element.ng-hide-add, .my-element.ng-hide-remove {
* transition:0.5s linear all;
* transition: 0.5s linear all;
* }
*
* .my-element.ng-hide-add { ... }
@@ -281,25 +281,25 @@ var ngShowDirective = ['$animate', function($animate) {
</file>
<file name="animations.css">
.animate-hide {
-webkit-transition:all linear 0.5s;
transition:all linear 0.5s;
line-height:20px;
opacity:1;
padding:10px;
border:1px solid black;
background:white;
-webkit-transition: all linear 0.5s;
transition: all linear 0.5s;
line-height: 20px;
opacity: 1;
padding: 10px;
border: 1px solid black;
background: white;
}
.animate-hide.ng-hide {
line-height:0;
opacity:0;
padding:0 10px;
line-height: 0;
opacity: 0;
padding: 0 10px;
}
.check-element {
padding:10px;
border:1px solid black;
background:white;
padding: 10px;
border: 1px solid black;
background: white;
}
</file>
<file name="protractor.js" type="protractor">
-1
View File
@@ -40,7 +40,6 @@ var scriptDirective = ['$templateCache', function($templateCache) {
compile: function(element, attr) {
if (attr.type == 'text/ng-template') {
var templateUrl = attr.id,
// IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent
text = element[0].text;
$templateCache.put(templateUrl, text);
+19 -12
View File
@@ -18,7 +18,7 @@ var ngOptionsMinErr = minErr('ngOptions');
* In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
* similar result. However, the `ngOptions` provides some benefits such as reducing memory and
* increasing speed by not creating a new scope for each repeated instance, as well as providing
* more flexibility in how the `select`'s model is assigned via `select as`. `ngOptions should be
* more flexibility in how the `select`'s model is assigned via `select as`. `ngOptions` should be
* used when the `select` model needs to be bound to a non-string value. This is because an option
* element can only be bound to string values at present.
*
@@ -212,7 +212,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
self.removeOption = function(value) {
if (this.hasOption(value)) {
delete optionsMap[value];
if (ngModelCtrl.$viewValue == value) {
if (ngModelCtrl.$viewValue === value) {
this.renderUnknownOption(value);
}
}
@@ -483,7 +483,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
if (multiple) {
return isDefined(selectedSet.remove(callExpression(compareValueFn, key, value)));
} else {
return viewValue == callExpression(compareValueFn, key, value);
return viewValue === callExpression(compareValueFn, key, value);
}
};
}
@@ -616,13 +616,14 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
lastElement = null; // start at the beginning
for (index = 0, length = optionGroup.length; index < length; index++) {
option = optionGroup[index];
if ((existingOption = existingOptions[index+1])) {
if ((existingOption = existingOptions[index + 1])) {
// reuse elements
lastElement = existingOption.element;
if (existingOption.label !== option.label) {
updateLabelMap(labelMap, existingOption.label, false);
updateLabelMap(labelMap, option.label, true);
lastElement.text(existingOption.label = option.label);
lastElement.prop('label', existingOption.label);
}
if (existingOption.id !== option.id) {
lastElement.val(existingOption.id = option.id);
@@ -652,6 +653,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
.val(option.id)
.prop('selected', option.selected)
.attr('selected', option.selected)
.prop('label', option.label)
.text(option.label);
}
@@ -677,18 +679,23 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
updateLabelMap(labelMap, option.label, false);
option.element.remove();
}
forEach(labelMap, function(count, label) {
if (count > 0) {
selectCtrl.addOption(label);
} else if (count < 0) {
selectCtrl.removeOption(label);
}
});
}
// remove any excessive OPTGROUPs from select
while (optionGroupsCache.length > groupIndex) {
optionGroupsCache.pop()[0].element.remove();
// remove all the labels in the option group
optionGroup = optionGroupsCache.pop();
for (index = 1; index < optionGroup.length; ++index) {
updateLabelMap(labelMap, optionGroup[index].label, false);
}
optionGroup[0].element.remove();
}
forEach(labelMap, function(count, label) {
if (count > 0) {
selectCtrl.addOption(label);
} else if (count < 0) {
selectCtrl.removeOption(label);
}
});
}
}
}
+2 -2
View File
@@ -147,8 +147,8 @@ function filterFilter() {
}
return false;
}
text = (''+text).toLowerCase();
return (''+obj).toLowerCase().indexOf(text) > -1;
text = ('' + text).toLowerCase();
return ('' + obj).toLowerCase().indexOf(text) > -1;
};
}
}
+11 -12
View File
@@ -11,7 +11,7 @@
*
* @param {number} amount Input to filter.
* @param {string=} symbol Currency symbol or identifier to be displayed.
* @param {number=} fractionSize Number of decimal places to round the amount to.
* @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
* @returns {string} Formatted number.
*
*
@@ -61,8 +61,7 @@ function currencyFilter($locale) {
}
if (isUndefined(fractionSize)) {
// TODO: read the default value from the locale file
fractionSize = 2;
fractionSize = formats.PATTERNS[1].maxFrac;
}
// if null or undefined pass it through
@@ -187,7 +186,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
if (whole.length >= (lgroup + group)) {
pos = whole.length - lgroup;
for (i = 0; i < pos; i++) {
if ((pos - i)%group === 0 && i !== 0) {
if ((pos - i) % group === 0 && i !== 0) {
formatedText += groupSep;
}
formatedText += whole.charAt(i);
@@ -195,7 +194,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
}
for (i = pos; i < whole.length; i++) {
if ((whole.length - i)%lgroup === 0 && i !== 0) {
if ((whole.length - i) % lgroup === 0 && i !== 0) {
formatedText += groupSep;
}
formatedText += whole.charAt(i);
@@ -214,9 +213,9 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
}
}
parts.push(isNegative ? pattern.negPre : pattern.posPre);
parts.push(formatedText);
parts.push(isNegative ? pattern.negSuf : pattern.posSuf);
parts.push(isNegative ? pattern.negPre : pattern.posPre,
formatedText,
isNegative ? pattern.negSuf : pattern.posSuf);
return parts.join('');
}
@@ -435,10 +434,10 @@ function dateFilter($locale) {
tzMin = int(match[9] + match[11]);
}
dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
var h = int(match[4]||0) - tzHour;
var m = int(match[5]||0) - tzMin;
var s = int(match[6]||0);
var ms = Math.round(parseFloat('0.' + (match[7]||0)) * 1000);
var h = int(match[4] || 0) - tzHour;
var m = int(match[5] || 0) - tzMin;
var s = int(match[6] || 0);
var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
timeSetter.call(date, h, m, s, ms);
return date;
}
+1 -1
View File
@@ -123,7 +123,7 @@ function limitToFilter() {
n = input.length;
}
for (; i<n; i++) {
for (; i < n; i++) {
out.push(input[i]);
}
+2 -4
View File
@@ -119,7 +119,7 @@ orderByFilter.$inject = ['$parse'];
function orderByFilter($parse) {
return function(array, sortPredicate, reverseOrder) {
if (!(isArrayLike(array))) return array;
sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate];
if (sortPredicate.length === 0) { sortPredicate = ['+']; }
sortPredicate = sortPredicate.map(function(predicate) {
var descending = false, get = predicate || identity;
@@ -146,9 +146,7 @@ function orderByFilter($parse) {
return compare(get(a),get(b));
}, descending);
});
var arrayCopy = [];
for (var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); }
return arrayCopy.sort(reverseComparator(comparator, reverseOrder));
return slice.call(array).sort(reverseComparator(comparator, reverseOrder));
function comparator(o1, o2) {
for (var i = 0; i < sortPredicate.length; i++) {
+36 -5
View File
@@ -11,7 +11,7 @@ function defaultHttpResponseTransform(data, headers) {
// strip json vulnerability protection prefix
data = data.replace(JSON_PROTECTION_PREFIX, '');
var contentType = headers('Content-Type');
if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0) ||
if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0 && data.trim()) ||
(JSON_START.test(data) && JSON_END.test(data))) {
data = fromJson(data);
}
@@ -26,7 +26,7 @@ function defaultHttpResponseTransform(data, headers) {
* @returns {Object} Parsed headers as key value object
*/
function parseHeaders(headers) {
var parsed = {}, key, val, i;
var parsed = createMap(), key, val, i;
if (!headers) return parsed;
@@ -63,7 +63,11 @@ function headersGetter(headers) {
if (!headersObj) headersObj = parseHeaders(headers);
if (name) {
return headersObj[lowercase(name)] || null;
var value = headersObj[lowercase(name)];
if (value === void 0) {
value = null;
}
return value;
}
return headersObj;
@@ -112,6 +116,11 @@ function $HttpProvider() {
*
* Object containing default values for all {@link ng.$http $http} requests.
*
* - **`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
* cache object will be cached. See {@link $http#caching $http Caching} for more information.
*
* - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
* Defaults value is `'XSRF-TOKEN'`.
*
@@ -125,6 +134,7 @@ function $HttpProvider() {
* - **`defaults.headers.post`**
* - **`defaults.headers.put`**
* - **`defaults.headers.patch`**
*
**/
var defaults = this.defaults = {
// transform incoming response data
@@ -339,6 +349,21 @@ function $HttpProvider() {
* In addition, you can supply a `headers` property in the config object passed when
* calling `$http(config)`, which overrides the defaults without changing them globally.
*
* To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
* Use the `headers` property, setting the desired header to `undefined`. For example:
*
* ```js
* var req = {
* method: 'POST',
* url: 'http://example.com',
* headers: {
* 'Content-Type': undefined
* },
* data: { test: 'test' },
* }
*
* $http(req).success(function(){...}).error(function(){...});
* ```
*
* ## Transforming Requests and Responses
*
@@ -585,12 +610,14 @@ function $HttpProvider() {
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}`
* transform function or an array of such functions. The transform function takes the http
* request body and headers and returns its transformed (typically serialized) version.
* See {@link #overriding-the-default-transformations-per-request Overriding the Default Transformations}
* See {@link ng.$http#overriding-the-default-transformations-per-request
* Overriding the Default Transformations}
* - **transformResponse**
* `{function(data, headersGetter)|Array.<function(data, headersGetter)>}`
* transform function or an array of such functions. The transform function takes the http
* response body and headers and returns its transformed (typically deserialized) version.
* See {@link #overriding-the-default-transformations-per-request Overriding the Default Transformations}
* See {@link ng.$http#overriding-the-default-transformations-per-request
* Overriding the Default Transformations}
* - **cache** `{boolean|Cache}` If true, a default $http cache will be used to cache the
* GET request, otherwise if a cache instance built with
* {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
@@ -718,6 +745,10 @@ function $HttpProvider() {
};
var headers = mergeHeaders(requestConfig);
if (!angular.isObject(requestConfig)) {
throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
}
extend(config, requestConfig);
config.headers = headers;
config.method = uppercase(config.method);
+3 -1
View File
@@ -126,7 +126,9 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
function completeRequest(callback, status, response, headersString, statusText) {
// cancel timeout and subsequent timeout promise resolution
timeoutId && $browserDefer.cancel(timeoutId);
if (timeoutId !== undefined) {
$browserDefer.cancel(timeoutId);
}
jsonpDone = xhr = null;
callback(status, response, headersString, statusText);
+2 -1
View File
@@ -301,7 +301,8 @@ function $InterpolateProvider() {
function parseStringifyInterceptor(value) {
try {
return stringify(getValue(value));
value = getValue(value);
return allOrNothing && !isDefined(value) ? value : stringify(value);
} catch (err) {
var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
err.toString());
+80 -17
View File
@@ -22,8 +22,8 @@ function encodePath(path) {
return segments.join('/');
}
function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) {
var parsedUrl = urlResolve(absoluteUrl, appBase);
function parseAbsoluteUrl(absoluteUrl, locationObj) {
var parsedUrl = urlResolve(absoluteUrl);
locationObj.$$protocol = parsedUrl.protocol;
locationObj.$$host = parsedUrl.hostname;
@@ -31,12 +31,12 @@ function parseAbsoluteUrl(absoluteUrl, locationObj, appBase) {
}
function parseAppUrl(relativeUrl, locationObj, appBase) {
function parseAppUrl(relativeUrl, locationObj) {
var prefixed = (relativeUrl.charAt(0) !== '/');
if (prefixed) {
relativeUrl = '/' + relativeUrl;
}
var match = urlResolve(relativeUrl, appBase);
var match = urlResolve(relativeUrl);
locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
match.pathname.substring(1) : match.pathname);
locationObj.$$search = parseKeyValue(match.search);
@@ -91,7 +91,7 @@ function LocationHtml5Url(appBase, basePrefix) {
this.$$html5 = true;
basePrefix = basePrefix || '';
var appBaseNoFile = stripFile(appBase);
parseAbsoluteUrl(appBase, this, appBase);
parseAbsoluteUrl(appBase, this);
/**
@@ -106,7 +106,7 @@ function LocationHtml5Url(appBase, basePrefix) {
appBaseNoFile);
}
parseAppUrl(pathUrl, this, appBase);
parseAppUrl(pathUrl, this);
if (!this.$$path) {
this.$$path = '/';
@@ -169,7 +169,7 @@ function LocationHtml5Url(appBase, basePrefix) {
function LocationHashbangUrl(appBase, hashPrefix) {
var appBaseNoFile = stripFile(appBase);
parseAbsoluteUrl(appBase, this, appBase);
parseAbsoluteUrl(appBase, this);
/**
@@ -189,7 +189,7 @@ function LocationHashbangUrl(appBase, hashPrefix) {
throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url,
hashPrefix);
}
parseAppUrl(withoutHashUrl, this, appBase);
parseAppUrl(withoutHashUrl, this);
this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
@@ -327,6 +327,13 @@ var locationPrototype = {
* Return full url representation with all segments encoded according to rules specified in
* [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo
* var absUrl = $location.absUrl();
* // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
* ```
*
* @return {string} full url
*/
absUrl: locationGetter('$$absUrl'),
@@ -342,6 +349,13 @@ var locationPrototype = {
*
* Change path, search and hash, when called with parameter and return `$location`.
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo
* var url = $location.url();
* // => "/some/path?foo=bar&baz=xoxo"
* ```
*
* @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
* @return {string} url
*/
@@ -350,8 +364,8 @@ var locationPrototype = {
return this.$$url;
var match = PATH_MATCH.exec(url);
if (match[1]) this.path(decodeURIComponent(match[1]));
if (match[2] || match[1]) this.search(match[3] || '');
if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
if (match[2] || match[1] || url === '') this.search(match[3] || '');
this.hash(match[5] || '');
return this;
@@ -366,6 +380,13 @@ var locationPrototype = {
*
* Return protocol of current url.
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo
* var protocol = $location.protocol();
* // => "http"
* ```
*
* @return {string} protocol of current url
*/
protocol: locationGetter('$$protocol'),
@@ -379,6 +400,13 @@ var locationPrototype = {
*
* Return host of current url.
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo
* var host = $location.host();
* // => "example.com"
* ```
*
* @return {string} host of current url.
*/
host: locationGetter('$$host'),
@@ -392,6 +420,13 @@ var locationPrototype = {
*
* Return port of current url.
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo
* var port = $location.port();
* // => 80
* ```
*
* @return {Number} port
*/
port: locationGetter('$$port'),
@@ -410,6 +445,13 @@ var locationPrototype = {
* Note: Path should always begin with forward slash (/), this method will add the forward slash
* if it is missing.
*
*
* ```js
* // given url http://example.com/#/some/path?foo=bar&baz=xoxo
* var path = $location.path();
* // => "/some/path"
* ```
*
* @param {(string|number)=} path New path
* @return {string} path
*/
@@ -435,10 +477,9 @@ var locationPrototype = {
* var searchObject = $location.search();
* // => {foo: 'bar', baz: 'xoxo'}
*
*
* // set foo to 'yipee'
* $location.search('foo', 'yipee');
* // => $location
* // $location.search() => {foo: 'yipee', baz: 'xoxo'}
* ```
*
* @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
@@ -508,6 +549,13 @@ var locationPrototype = {
*
* Change hash fragment when called with parameter and return `$location`.
*
*
* ```js
* // given url http://example.com/some/path?foo=bar&baz=xoxo#hashValue
* var hash = $location.hash();
* // => "hashValue"
* ```
*
* @param {(string|number)=} hash New hash fragment
* @return {string} hash
*/
@@ -829,11 +877,19 @@ function $LocationProvider() {
$rootScope.$evalAsync(function() {
var oldUrl = $location.absUrl();
var oldState = $location.$$state;
var defaultPrevented;
$location.$$parse(newUrl);
$location.$$state = newState;
if ($rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
newState, oldState).defaultPrevented) {
defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
newState, oldState).defaultPrevented;
// if the location was changed by a `$locationChangeStart` handler then stop
// processing this location change
if ($location.absUrl() !== newUrl) return;
if (defaultPrevented) {
$location.$$parse(oldUrl);
$location.$$state = oldState;
setBrowserUrlWithFallback(oldUrl, false, oldState);
@@ -857,13 +913,20 @@ function $LocationProvider() {
initializing = false;
$rootScope.$evalAsync(function() {
if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl,
$location.$$state, oldState).defaultPrevented) {
var newUrl = $location.absUrl();
var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
$location.$$state, oldState).defaultPrevented;
// if the location was changed by a `$locationChangeStart` handler then stop
// processing this location change
if ($location.absUrl() !== newUrl) return;
if (defaultPrevented) {
$location.$$parse(oldUrl);
$location.$$state = oldState;
} else {
if (urlOrStateChanged) {
setBrowserUrlWithFallback($location.absUrl(), currentReplace,
setBrowserUrlWithFallback(newUrl, currentReplace,
oldState === $location.$$state ? null : $location.$$state);
}
afterLocationChange(oldUrl, oldState);
+183 -170
View File
@@ -5,7 +5,7 @@ var $parseMinErr = minErr('$parse');
// Sandboxing Angular Expressions
// ------------------------------
// Angular expressions are generally considered safe because these expressions only have direct
// access to $scope and locals. However, one can obtain the ability to execute arbitrary JS code by
// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
// obtaining a reference to native JS functions such as the Function constructor.
//
// As an example, consider the following Angular expression:
@@ -14,7 +14,7 @@ var $parseMinErr = minErr('$parse');
//
// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
// against the expression language, but not to prevent exploits that were enabled by exposing
// sensitive JavaScript or browser apis on Scope. Exposing such objects on a Scope is never a good
// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
// practice and therefore we are not even trying to protect against interaction with an object
// explicitly exposed in this way.
//
@@ -22,6 +22,8 @@ var $parseMinErr = minErr('$parse');
// window or some DOM object that has a reference to window is published onto a Scope.
// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
// native objects.
//
// See https://docs.angularjs.org/guide/security
function ensureSafeMemberName(name, fullExpression) {
@@ -30,7 +32,7 @@ function ensureSafeMemberName(name, fullExpression) {
|| name === "__proto__") {
throw $parseMinErr('isecfld',
'Attempting to access a disallowed field in Angular expressions! '
+'Expression: {0}', fullExpression);
+ 'Expression: {0}', fullExpression);
}
return name;
}
@@ -107,24 +109,24 @@ var OPERATORS = extend(createMap(), {
}
return a;
}
return isDefined(b)?b:undefined;},
return isDefined(b) ? b : undefined;},
'-':function(self, locals, a, b) {
a=a(self, locals); b=b(self, locals);
return (isDefined(a)?a:0)-(isDefined(b)?b:0);
return (isDefined(a) ? a : 0) - (isDefined(b) ? b : 0);
},
'*':function(self, locals, a, b) {return a(self, locals)*b(self, locals);},
'/':function(self, locals, a, b) {return a(self, locals)/b(self, locals);},
'%':function(self, locals, a, b) {return a(self, locals)%b(self, locals);},
'===':function(self, locals, a, b) {return a(self, locals)===b(self, locals);},
'!==':function(self, locals, a, b) {return a(self, locals)!==b(self, locals);},
'==':function(self, locals, a, b) {return a(self, locals)==b(self, locals);},
'!=':function(self, locals, a, b) {return a(self, locals)!=b(self, locals);},
'<':function(self, locals, a, b) {return a(self, locals)<b(self, locals);},
'>':function(self, locals, a, b) {return a(self, locals)>b(self, locals);},
'<=':function(self, locals, a, b) {return a(self, locals)<=b(self, locals);},
'>=':function(self, locals, a, b) {return a(self, locals)>=b(self, locals);},
'&&':function(self, locals, a, b) {return a(self, locals)&&b(self, locals);},
'||':function(self, locals, a, b) {return a(self, locals)||b(self, locals);},
'*':function(self, locals, a, b) {return a(self, locals) * b(self, locals);},
'/':function(self, locals, a, b) {return a(self, locals) / b(self, locals);},
'%':function(self, locals, a, b) {return a(self, locals) % b(self, locals);},
'===':function(self, locals, a, b) {return a(self, locals) === b(self, locals);},
'!==':function(self, locals, a, b) {return a(self, locals) !== b(self, locals);},
'==':function(self, locals, a, b) {return a(self, locals) == b(self, locals);},
'!=':function(self, locals, a, b) {return a(self, locals) != b(self, locals);},
'<':function(self, locals, a, b) {return a(self, locals) < b(self, locals);},
'>':function(self, locals, a, b) {return a(self, locals) > b(self, locals);},
'<=':function(self, locals, a, b) {return a(self, locals) <= b(self, locals);},
'>=':function(self, locals, a, b) {return a(self, locals) >= b(self, locals);},
'&&':function(self, locals, a, b) {return a(self, locals) && b(self, locals);},
'||':function(self, locals, a, b) {return a(self, locals) || b(self, locals);},
'!':function(self, locals, a) {return !a(self, locals);},
//Tokenized as operators but parsed as assignment/filters
@@ -150,44 +152,31 @@ Lexer.prototype = {
lex: function(text) {
this.text = text;
this.index = 0;
this.ch = undefined;
this.tokens = [];
while (this.index < this.text.length) {
this.ch = this.text.charAt(this.index);
if (this.is('"\'')) {
this.readString(this.ch);
} else if (this.isNumber(this.ch) || this.is('.') && this.isNumber(this.peek())) {
var ch = this.text.charAt(this.index);
if (ch === '"' || ch === "'") {
this.readString(ch);
} else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
this.readNumber();
} else if (this.isIdent(this.ch)) {
} else if (this.isIdent(ch)) {
this.readIdent();
} else if (this.is('(){}[].,;:?')) {
this.tokens.push({
index: this.index,
text: this.ch
});
} else if (this.is(ch, '(){}[].,;:?')) {
this.tokens.push({index: this.index, text: ch});
this.index++;
} else if (this.isWhitespace(this.ch)) {
} else if (this.isWhitespace(ch)) {
this.index++;
} else {
var ch2 = this.ch + this.peek();
var ch2 = ch + this.peek();
var ch3 = ch2 + this.peek(2);
var fn = OPERATORS[this.ch];
var fn2 = OPERATORS[ch2];
var fn3 = OPERATORS[ch3];
if (fn3) {
this.tokens.push({index: this.index, text: ch3, fn: fn3});
this.index += 3;
} else if (fn2) {
this.tokens.push({index: this.index, text: ch2, fn: fn2});
this.index += 2;
} else if (fn) {
this.tokens.push({
index: this.index,
text: this.ch,
fn: fn
});
this.index += 1;
var op1 = OPERATORS[ch];
var op2 = OPERATORS[ch2];
var op3 = OPERATORS[ch3];
if (op1 || op2 || op3) {
var token = op3 ? ch3 : (op2 ? ch2 : ch);
this.tokens.push({index: this.index, text: token, operator: true});
this.index += token.length;
} else {
this.throwError('Unexpected next character ', this.index, this.index + 1);
}
@@ -196,8 +185,8 @@ Lexer.prototype = {
return this.tokens;
},
is: function(chars) {
return chars.indexOf(this.ch) !== -1;
is: function(ch, chars) {
return chars.indexOf(ch) !== -1;
},
peek: function(i) {
@@ -206,7 +195,7 @@ Lexer.prototype = {
},
isNumber: function(ch) {
return ('0' <= ch && ch <= '9');
return ('0' <= ch && ch <= '9') && typeof ch === "string";
},
isWhitespace: function(ch) {
@@ -259,79 +248,28 @@ Lexer.prototype = {
}
this.index++;
}
number = 1 * number;
this.tokens.push({
index: start,
text: number,
constant: true,
fn: function() { return number; }
value: Number(number)
});
},
readIdent: function() {
var expression = this.text;
var ident = '';
var start = this.index;
var lastDot, peekIndex, methodName, ch;
while (this.index < this.text.length) {
ch = this.text.charAt(this.index);
if (ch === '.' || this.isIdent(ch) || this.isNumber(ch)) {
if (ch === '.') lastDot = this.index;
ident += ch;
} else {
var ch = this.text.charAt(this.index);
if (!(this.isIdent(ch) || this.isNumber(ch))) {
break;
}
this.index++;
}
//check if the identifier ends with . and if so move back one char
if (lastDot && ident[ident.length - 1] === '.') {
this.index--;
ident = ident.slice(0, -1);
lastDot = ident.lastIndexOf('.');
if (lastDot === -1) {
lastDot = undefined;
}
}
//check if this is not a method invocation and if it is back out to last dot
if (lastDot) {
peekIndex = this.index;
while (peekIndex < this.text.length) {
ch = this.text.charAt(peekIndex);
if (ch === '(') {
methodName = ident.substr(lastDot - start + 1);
ident = ident.substr(0, lastDot - start);
this.index = peekIndex;
break;
}
if (this.isWhitespace(ch)) {
peekIndex++;
} else {
break;
}
}
}
this.tokens.push({
index: start,
text: ident,
fn: CONSTANTS[ident] || getterFn(ident, this.options, expression)
text: this.text.slice(start, this.index),
identifier: true
});
if (methodName) {
this.tokens.push({
index: lastDot,
text: '.'
});
this.tokens.push({
index: lastDot + 1,
text: methodName
});
}
},
readString: function(quote) {
@@ -362,9 +300,8 @@ Lexer.prototype = {
this.tokens.push({
index: start,
text: rawString,
string: string,
constant: true,
fn: function() { return string; }
value: string
});
return;
} else {
@@ -425,16 +362,12 @@ Parser.prototype = {
primary = this.arrayDeclaration();
} else if (this.expect('{')) {
primary = this.object();
} else if (this.peek().identifier) {
primary = this.identifier();
} else if (this.peek().constant) {
primary = this.constant();
} else {
var token = this.expect();
primary = token.fn;
if (!primary) {
this.throwError('not a primary expression', token);
}
if (token.constant) {
primary.constant = true;
primary.literal = true;
}
this.throwError('not a primary expression', this.peek());
}
var next, context;
@@ -468,8 +401,11 @@ Parser.prototype = {
},
peek: function(e1, e2, e3, e4) {
if (this.tokens.length > 0) {
var token = this.tokens[0];
return this.peekAhead(0, e1, e2, e3, e4);
},
peekAhead: function(i, e1, e2, e3, e4) {
if (this.tokens.length > i) {
var token = this.tokens[i];
var t = token.text;
if (t === e1 || t === e2 || t === e3 || t === e4 ||
(!e1 && !e2 && !e3 && !e4)) {
@@ -489,12 +425,19 @@ Parser.prototype = {
},
consume: function(e1) {
if (!this.expect(e1)) {
if (this.tokens.length === 0) {
throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
}
var token = this.expect(e1);
if (!token) {
this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
}
return token;
},
unaryFn: function(fn, right) {
unaryFn: function(op, right) {
var fn = OPERATORS[op];
return extend(function $parseUnaryFn(self, locals) {
return fn(self, locals, right);
}, {
@@ -503,7 +446,8 @@ Parser.prototype = {
});
},
binaryFn: function(left, fn, right, isBranching) {
binaryFn: function(left, op, right, isBranching) {
var fn = OPERATORS[op];
return extend(function $parseBinaryFn(self, locals) {
return fn(self, locals, left, right);
}, {
@@ -512,6 +456,28 @@ Parser.prototype = {
});
},
identifier: function() {
var id = this.consume().text;
//Continue reading each `.identifier` unless it is a method invocation
while (this.peek('.') && this.peekAhead(1).identifier && !this.peekAhead(2, '(')) {
id += this.consume().text + this.consume().text;
}
return CONSTANTS[id] || getterFn(id, this.options, this.text);
},
constant: function() {
var value = this.consume().value;
return extend(function $parseConstant() {
return value;
}, {
constant: true,
literal: true
});
},
statements: function() {
var statements = [];
while (true) {
@@ -543,8 +509,7 @@ Parser.prototype = {
},
filter: function(inputFn) {
var token = this.expect();
var fn = this.$filter(token.text);
var fn = this.$filter(this.consume().text);
var argsFn;
var args;
@@ -607,7 +572,7 @@ Parser.prototype = {
var token;
if ((token = this.expect('?'))) {
middle = this.assignment();
if ((token = this.expect(':'))) {
if (this.consume(':')) {
var right = this.assignment();
return extend(function $parseTernary(self, locals) {
@@ -615,9 +580,6 @@ Parser.prototype = {
}, {
constant: left.constant && middle.constant && right.constant
});
} else {
this.throwError('expected :', token);
}
}
@@ -628,7 +590,7 @@ Parser.prototype = {
var left = this.logicalAND();
var token;
while ((token = this.expect('||'))) {
left = this.binaryFn(left, token.fn, this.logicalAND(), true);
left = this.binaryFn(left, token.text, this.logicalAND(), true);
}
return left;
},
@@ -637,7 +599,7 @@ Parser.prototype = {
var left = this.equality();
var token;
if ((token = this.expect('&&'))) {
left = this.binaryFn(left, token.fn, this.logicalAND(), true);
left = this.binaryFn(left, token.text, this.logicalAND(), true);
}
return left;
},
@@ -646,7 +608,7 @@ Parser.prototype = {
var left = this.relational();
var token;
if ((token = this.expect('==','!=','===','!=='))) {
left = this.binaryFn(left, token.fn, this.equality());
left = this.binaryFn(left, token.text, this.equality());
}
return left;
},
@@ -655,7 +617,7 @@ Parser.prototype = {
var left = this.additive();
var token;
if ((token = this.expect('<', '>', '<=', '>='))) {
left = this.binaryFn(left, token.fn, this.relational());
left = this.binaryFn(left, token.text, this.relational());
}
return left;
},
@@ -664,7 +626,7 @@ Parser.prototype = {
var left = this.multiplicative();
var token;
while ((token = this.expect('+','-'))) {
left = this.binaryFn(left, token.fn, this.multiplicative());
left = this.binaryFn(left, token.text, this.multiplicative());
}
return left;
},
@@ -673,7 +635,7 @@ Parser.prototype = {
var left = this.unary();
var token;
while ((token = this.expect('*','/','%'))) {
left = this.binaryFn(left, token.fn, this.unary());
left = this.binaryFn(left, token.text, this.unary());
}
return left;
},
@@ -683,9 +645,9 @@ Parser.prototype = {
if (this.expect('+')) {
return this.primary();
} else if ((token = this.expect('-'))) {
return this.binaryFn(Parser.ZERO, token.fn, this.unary());
return this.binaryFn(Parser.ZERO, token.text, this.unary());
} else if ((token = this.expect('!'))) {
return this.unaryFn(token.fn, this.unary());
return this.unaryFn(token.text, this.unary());
} else {
return this.primary();
}
@@ -693,7 +655,7 @@ Parser.prototype = {
fieldAccess: function(object) {
var expression = this.text;
var field = this.expect().text;
var field = this.consume().text;
var getter = getterFn(field, this.options, expression);
return extend(function $parseFieldAccess(scope, locals, self) {
@@ -760,13 +722,13 @@ Parser.prototype = {
ensureSafeObject(context, expressionText);
ensureSafeFunction(fn, expressionText);
// IE stupidity! (IE doesn't have apply for some native functions)
// IE doesn't have apply for some native functions
var v = fn.apply
? fn.apply(context, args)
: fn(args[0], args[1], args[2], args[3], args[4]);
return ensureSafeObject(v, expressionText);
};
};
},
// This is used with json array declaration
@@ -778,8 +740,7 @@ Parser.prototype = {
// Support trailing commas per ES5.1.
break;
}
var elementFn = this.expression();
elementFns.push(elementFn);
elementFns.push(this.expression());
} while (this.expect(','));
}
this.consume(']');
@@ -805,11 +766,16 @@ Parser.prototype = {
// Support trailing commas per ES5.1.
break;
}
var token = this.expect();
keys.push(token.string || token.text);
var token = this.consume();
if (token.constant) {
keys.push(token.value);
} else if (token.identifier) {
keys.push(token.text);
} else {
this.throwError("invalid key", token);
}
this.consume(':');
var value = this.expression();
valueFns.push(value);
valueFns.push(this.expression());
} while (this.expect(','));
}
this.consume('}');
@@ -852,64 +818,85 @@ function setter(obj, path, setValue, fullExp) {
return setValue;
}
var getterFnCache = createMap();
var getterFnCacheDefault = createMap();
var getterFnCacheExpensive = createMap();
function isPossiblyDangerousMemberName(name) {
return name == 'constructor';
}
/**
* Implementation of the "Black Hole" variant from:
* - http://jsperf.com/angularjs-parse-getter/4
* - http://jsperf.com/path-evaluation-simplified/7
*/
function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp) {
function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, expensiveChecks) {
ensureSafeMemberName(key0, fullExp);
ensureSafeMemberName(key1, fullExp);
ensureSafeMemberName(key2, fullExp);
ensureSafeMemberName(key3, fullExp);
ensureSafeMemberName(key4, fullExp);
var eso = function(o) {
return ensureSafeObject(o, fullExp);
};
var eso0 = (expensiveChecks || isPossiblyDangerousMemberName(key0)) ? eso : identity;
var eso1 = (expensiveChecks || isPossiblyDangerousMemberName(key1)) ? eso : identity;
var eso2 = (expensiveChecks || isPossiblyDangerousMemberName(key2)) ? eso : identity;
var eso3 = (expensiveChecks || isPossiblyDangerousMemberName(key3)) ? eso : identity;
var eso4 = (expensiveChecks || isPossiblyDangerousMemberName(key4)) ? eso : identity;
return function cspSafeGetter(scope, locals) {
var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
if (pathVal == null) return pathVal;
pathVal = pathVal[key0];
pathVal = eso0(pathVal[key0]);
if (!key1) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key1];
pathVal = eso1(pathVal[key1]);
if (!key2) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key2];
pathVal = eso2(pathVal[key2]);
if (!key3) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key3];
pathVal = eso3(pathVal[key3]);
if (!key4) return pathVal;
if (pathVal == null) return undefined;
pathVal = pathVal[key4];
pathVal = eso4(pathVal[key4]);
return pathVal;
};
}
function getterFn(path, options, fullExp) {
var fn = getterFnCache[path];
function getterFnWithEnsureSafeObject(fn, fullExpression) {
return function(s, l) {
return fn(s, l, ensureSafeObject, fullExpression);
};
}
function getterFn(path, options, fullExp) {
var expensiveChecks = options.expensiveChecks;
var getterFnCache = (expensiveChecks ? getterFnCacheExpensive : getterFnCacheDefault);
var fn = getterFnCache[path];
if (fn) return fn;
var pathKeys = path.split('.'),
pathKeysLength = pathKeys.length;
// http://jsperf.com/angularjs-parse-getter/6
if (options.csp) {
if (pathKeysLength < 6) {
fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp);
fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, expensiveChecks);
} else {
fn = function cspSafeGetter(scope, locals) {
var i = 0, val;
do {
val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++],
pathKeys[i++], fullExp)(scope, locals);
pathKeys[i++], fullExp, expensiveChecks)(scope, locals);
locals = undefined; // clear after first iteration
scope = val;
@@ -919,22 +906,33 @@ function getterFn(path, options, fullExp) {
}
} else {
var code = '';
if (expensiveChecks) {
code += 's = eso(s, fe);\nl = eso(l, fe);\n';
}
var needsEnsureSafeObject = expensiveChecks;
forEach(pathKeys, function(key, index) {
ensureSafeMemberName(key, fullExp);
code += 'if(s == null) return undefined;\n' +
's='+ (index
var lookupJs = (index
// we simply dereference 's' on any .dot notation
? 's'
// but if we are first then we check locals first, and if so read it first
: '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '.' + key + ';\n';
: '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '.' + key;
if (expensiveChecks || isPossiblyDangerousMemberName(key)) {
lookupJs = 'eso(' + lookupJs + ', fe)';
needsEnsureSafeObject = true;
}
code += 'if(s == null) return undefined;\n' +
's=' + lookupJs + ';\n';
});
code += 'return s;';
/* jshint -W054 */
var evaledFnGetter = new Function('s', 'l', code); // s=scope, l=locals
var evaledFnGetter = new Function('s', 'l', 'eso', 'fe', code); // s=scope, l=locals, eso=ensureSafeObject
/* jshint +W054 */
evaledFnGetter.toString = valueFn(code);
if (needsEnsureSafeObject) {
evaledFnGetter = getterFnWithEnsureSafeObject(evaledFnGetter, fullExp);
}
fn = evaledFnGetter;
}
@@ -1004,15 +1002,20 @@ function getValueOf(value) {
* service.
*/
function $ParseProvider() {
var cache = createMap();
var cacheDefault = createMap();
var cacheExpensive = createMap();
var $parseOptions = {
csp: false
};
this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
$parseOptions.csp = $sniffer.csp;
var $parseOptions = {
csp: $sniffer.csp,
expensiveChecks: false
},
$parseOptionsExpensive = {
csp: $sniffer.csp,
expensiveChecks: true
};
function wrapSharedExpression(exp) {
var wrapped = exp;
@@ -1029,13 +1032,14 @@ function $ParseProvider() {
return wrapped;
}
return function $parse(exp, interceptorFn) {
return function $parse(exp, interceptorFn, expensiveChecks) {
var parsedExpression, oneTime, cacheKey;
switch (typeof exp) {
case 'string':
cacheKey = exp = exp.trim();
var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
parsedExpression = cache[cacheKey];
if (!parsedExpression) {
@@ -1044,8 +1048,9 @@ function $ParseProvider() {
exp = exp.substring(2);
}
var lexer = new Lexer($parseOptions);
var parser = new Parser(lexer, $filter, $parseOptions);
var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
var lexer = new Lexer(parseOptions);
var parser = new Parser(lexer, $filter, parseOptions);
parsedExpression = parser.parse(exp);
if (parsedExpression.constant) {
@@ -1212,8 +1217,16 @@ function $ParseProvider() {
function addInterceptor(parsedExpression, interceptorFn) {
if (!interceptorFn) return parsedExpression;
var watchDelegate = parsedExpression.$$watchDelegate;
var fn = function interceptedExpression(scope, locals) {
var regularWatch =
watchDelegate !== oneTimeLiteralWatchDelegate &&
watchDelegate !== oneTimeWatchDelegate;
var fn = regularWatch ? function regularInterceptedExpression(scope, locals) {
var value = parsedExpression(scope, locals);
return interceptorFn(value, scope, locals);
} : function oneTimeInterceptedExpression(scope, locals) {
var value = parsedExpression(scope, locals);
var result = interceptorFn(value, scope, locals);
// we only return the interceptor's result if the
+6 -6
View File
@@ -6,7 +6,11 @@
* @requires $rootScope
*
* @description
* A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q).
* A service that helps you run functions asynchronously, and use their return values (or exceptions)
* when they are done processing.
*
* This is an implementation of promises/deferred objects inspired by
* [Kris Kowal's Q](https://github.com/kriskowal/q).
*
* $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
* implementations, and the other which resembles ES6 promises to some degree.
@@ -142,16 +146,12 @@
*
* - `catch(errorCallback)` shorthand for `promise.then(null, errorCallback)`
*
* - `finally(callback)` allows you to observe either the fulfillment or rejection of a promise,
* - `finally(callback, notifyCallback)` allows you to observe either the fulfillment or rejection of a promise,
* but to do so without modifying the final value. This is useful to release resources or do some
* clean-up that needs to be done whether the promise was rejected or resolved. See the [full
* specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
* more information.
*
* Because `finally` is a reserved word in JavaScript and reserved keywords are not supported as
* property names by ES3, you'll need to invoke the method like `promise['finally'](callback)` to
* make your code IE8 and Android 2.x compatible.
*
* # Chaining promises
*
* Because calling the `then` method of a promise returns a new derived promise, it is easily
+3 -3
View File
@@ -7,9 +7,9 @@
* @description
* The root element of Angular application. This is either the element where {@link
* ng.directive:ngApp ngApp} was declared or the element passed into
* {@link angular.bootstrap}. The element represent the root element of application. It is also the
* location where the applications {@link auto.$injector $injector} service gets
* published, it can be retrieved using `$rootElement.injector()`.
* {@link angular.bootstrap}. The element represents the root element of application. It is also the
* location where the application's {@link auto.$injector $injector} service gets
* published, and can be retrieved using `$rootElement.injector()`.
*/
+15 -8
View File
@@ -113,6 +113,10 @@ function $RootScopeProvider() {
expect(parent.salutation).toEqual('Hello');
* ```
*
* When interacting with `Scope` in tests, additional helper methods are available on the
* instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
* details.
*
*
* @param {Object.<string, function()>=} providers Map of service factory which need to be
* provided for the current scope. Defaults to {@link ng}.
@@ -551,6 +555,9 @@ function $RootScopeProvider() {
newValue = _value;
var newLength, key, bothNaN, newItem, oldItem;
// If the new value is undefined, then return undefined as the watch may be a one-time watch
if (isUndefined(newValue)) return;
if (!isObject(newValue)) { // if primitive
if (oldValue !== newValue) {
oldValue = newValue;
@@ -766,11 +773,11 @@ function $RootScopeProvider() {
if (ttl < 5) {
logIdx = 4 - ttl;
if (!watchLog[logIdx]) watchLog[logIdx] = [];
logMsg = (isFunction(watch.exp))
? 'fn: ' + (watch.exp.name || watch.exp.toString())
: watch.exp;
logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
watchLog[logIdx].push(logMsg);
watchLog[logIdx].push({
msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
newVal: value,
oldVal: last
});
}
} else if (watch === lastDirtyWatch) {
// If the most recently dirty watcher is now clean, short circuit since the remaining watchers
@@ -803,7 +810,7 @@ function $RootScopeProvider() {
throw $rootScopeMinErr('infdig',
'{0} $digest() iterations reached. Aborting!\n' +
'Watchers fired in the last 5 iterations: {1}',
TTL, toJson(watchLog));
TTL, watchLog);
}
} while (dirty || asyncQueue.length);
@@ -1154,7 +1161,7 @@ function $RootScopeProvider() {
do {
namedListeners = scope.$$listeners[name] || empty;
event.currentScope = scope;
for (i=0, length=namedListeners.length; i<length; i++) {
for (i = 0, length = namedListeners.length; i < length; i++) {
// if listeners were deregistered, defragment the array
if (!namedListeners[i]) {
@@ -1228,7 +1235,7 @@ function $RootScopeProvider() {
while ((current = next)) {
event.currentScope = current;
listeners = current.$$listeners[name] || [];
for (i=0, length = listeners.length; i<length; i++) {
for (i = 0, length = listeners.length; i < length; i++) {
// if listeners were deregistered, defragment the array
if (!listeners[i]) {
listeners.splice(i, 1);
+1 -1
View File
@@ -63,7 +63,7 @@ function $$SanitizeUriProvider() {
var normalizedVal;
normalizedVal = urlResolve(uri).href;
if (normalizedVal !== '' && !normalizedVal.match(regex)) {
return 'unsafe:'+normalizedVal;
return 'unsafe:' + normalizedVal;
}
return uri;
};
+1 -1
View File
@@ -44,7 +44,7 @@ function $SnifferProvider() {
transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
if (android && (!transitions||!animations)) {
if (android && (!transitions || !animations)) {
transitions = isString(document.body.style.webkitTransition);
animations = isString(document.body.style.webkitAnimation);
}
+5 -10
View File
@@ -28,14 +28,9 @@ function $TemplateRequestProvider() {
var transformResponse = $http.defaults && $http.defaults.transformResponse;
if (isArray(transformResponse)) {
var original = transformResponse;
transformResponse = [];
for (var i=0; i<original.length; ++i) {
var transformer = original[i];
if (transformer !== defaultHttpResponseTransform) {
transformResponse.push(transformer);
}
}
transformResponse = transformResponse.filter(function(transformer) {
return transformer !== defaultHttpResponseTransform;
});
} else if (transformResponse === defaultHttpResponseTransform) {
transformResponse = null;
}
@@ -53,12 +48,12 @@ function $TemplateRequestProvider() {
return html;
}, handleError);
function handleError() {
function handleError(resp) {
self.totalPendingRequests--;
if (!ignoreRequestError) {
throw $compileMinErr('tpload', 'Failed to load template: {0}', tpl);
}
return $q.reject();
return $q.reject(resp);
}
}
+2 -2
View File
@@ -7,7 +7,7 @@
// exactly the behavior needed here. There is little value is mocking these out for this
// service.
var urlParsingNode = document.createElement("a");
var originUrl = urlResolve(window.location.href, true);
var originUrl = urlResolve(window.location.href);
/**
@@ -62,7 +62,7 @@ var originUrl = urlResolve(window.location.href, true);
* | pathname | The pathname, beginning with "/"
*
*/
function urlResolve(url, base) {
function urlResolve(url) {
var href = url;
if (msie) {
+14 -14
View File
@@ -239,7 +239,7 @@
* You then configure `$animate` to enforce this prefix:
*
* ```js
* $animateProvider.classNamePrefix(/animate-/);
* $animateProvider.classNameFilter(/animate-/);
* ```
* </div>
*
@@ -970,9 +970,9 @@ angular.module('ngAnimate', ['ng'])
* | 3. $animate waits for the next digest to start the animation | class="my-animation ng-animate" |
* | 4. the .ng-leave class is added to the element | class="my-animation ng-animate ng-leave" |
* | 5. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-leave" |
* | 6. $animate blocks all CSS transitions on the element to ensure the .ng-leave class styling is applied right away | class="my-animation ng-animate ng-leave |
* | 6. $animate blocks all CSS transitions on the element to ensure the .ng-leave class styling is applied right away | class="my-animation ng-animate ng-leave" |
* | 7. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate ng-leave" |
* | 8. $animate removes the CSS transition block placed on the element | class="my-animation ng-animate ng-leave |
* | 8. $animate removes the CSS transition block placed on the element | class="my-animation ng-animate ng-leave" |
* | 9. the .ng-leave-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-leave ng-leave-active" |
* | 10. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate ng-leave ng-leave-active" |
* | 11. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
@@ -1016,9 +1016,9 @@ angular.module('ngAnimate', ['ng'])
* | 4. $animate runs the JavaScript-defined animations detected on the element | class="my-animation ng-animate" |
* | 5. the .ng-move class is added to the element | class="my-animation ng-animate ng-move" |
* | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate ng-move" |
* | 7. $animate blocks all CSS transitions on the element to ensure the .ng-move class styling is applied right away | class="my-animation ng-animate ng-move |
* | 7. $animate blocks all CSS transitions on the element to ensure the .ng-move class styling is applied right away | class="my-animation ng-animate ng-move" |
* | 8. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate ng-move" |
* | 9. $animate removes the CSS transition block placed on the element | class="my-animation ng-animate ng-move |
* | 9. $animate removes the CSS transition block placed on the element | class="my-animation ng-animate ng-move" |
* | 10. the .ng-move-active class is added (this triggers the CSS transition/animation) | class="my-animation ng-animate ng-move ng-move-active" |
* | 11. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate ng-move ng-move-active" |
* | 12. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
@@ -1063,8 +1063,8 @@ angular.module('ngAnimate', ['ng'])
* | 3. the .super-add class is added to the element | class="my-animation ng-animate super-add" |
* | 4. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate super-add" |
* | 5. the .super and .super-add-active classes are added (this triggers the CSS transition/animation) | class="my-animation ng-animate super super-add super-add-active" |
* | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super-add" |
* | 7. $animate waits for the animation to complete (via events and timeout) | class="my-animation super super-add super-add-active" |
* | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super super-add super-add-active" |
* | 7. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate super super-add super-add-active" |
* | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation super" |
* | 9. The super class is kept on the element | class="my-animation super" |
* | 10. The returned promise is resolved. | class="my-animation super" |
@@ -1097,7 +1097,7 @@ angular.module('ngAnimate', ['ng'])
* | 3. the .super-remove class is added to the element | class="my-animation super ng-animate super-remove" |
* | 4. $animate waits for a single animation frame (this performs a reflow) | class="my-animation super ng-animate super-remove" |
* | 5. the .super-remove-active classes are added and .super is removed (this triggers the CSS transition/animation) | class="my-animation ng-animate super-remove super-remove-active" |
* | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation super ng-animate super-remove" |
* | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate super-remove super-remove-active" |
* | 7. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate super-remove super-remove-active" |
* | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation" |
* | 9. The returned promise is resolved. | class="my-animation" |
@@ -1122,11 +1122,11 @@ angular.module('ngAnimate', ['ng'])
*
* | Animation Step | What the element class attribute looks like |
* |--------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------|
* | 1. $animate.removeClass(element, on, off) is called | class="my-animation super off” |
* | 2. $animate runs the JavaScript-defined animations detected on the element | class="my-animation super ng-animate off |
* | 3. the .on-add and .off-remove classes are added to the element | class="my-animation ng-animate on-add off-remove off |
* | 4. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate on-add off-remove off |
* | 5. the .on, .on-add-active and .off-remove-active classes are added and .off is removed (this triggers the CSS transition/animation) | class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active |
* | 1. $animate.setClass(element, 'on', 'off') is called | class="my-animation off" |
* | 2. $animate runs the JavaScript-defined animations detected on the element | class="my-animation ng-animate off" |
* | 3. the .on-add and .off-remove classes are added to the element | class="my-animation ng-animate on-add off-remove off" |
* | 4. $animate waits for a single animation frame (this performs a reflow) | class="my-animation ng-animate on-add off-remove off" |
* | 5. the .on, .on-add-active and .off-remove-active classes are added and .off is removed (this triggers the CSS transition/animation) | class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active" |
* | 6. $animate scans the element styles to get the CSS transition/animation duration and delay | class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active" |
* | 7. $animate waits for the animation to complete (via events and timeout) | class="my-animation ng-animate on on-add on-add-active off-remove off-remove-active" |
* | 8. The animation ends and all generated CSS classes are removed from the element | class="my-animation on" |
@@ -1883,7 +1883,7 @@ angular.module('ngAnimate', ['ng'])
//the jqLite object, so we're safe to use a single variable to house
//the styles since there is always only one element being animated
var oldStyle = node.getAttribute('style') || '';
if (oldStyle.charAt(oldStyle.length-1) !== ';') {
if (oldStyle.charAt(oldStyle.length - 1) !== ';') {
oldStyle += ';';
}
node.setAttribute('style', oldStyle + ' ' + style);
+93 -22
View File
@@ -5,33 +5,49 @@
* @name ngAria
* @description
*
* The `ngAria` module provides support for adding <abbr title="Accessible Rich Internet Applications">ARIA</abbr>
* attributes that convey state or semantic information about the application in order to allow assistive technologies
* to convey appropriate information to persons with disabilities.
* The `ngAria` module provides support for common
* [<abbr title="Accessible Rich Internet Applications">ARIA</abbr>](http://www.w3.org/TR/wai-aria/)
* attributes that convey state or semantic information about the application for users
* of assistive technologies, such as screen readers.
*
* <div doc-module-components="ngAria"></div>
*
* # Usage
* To enable the addition of the ARIA tags, just require the module into your application and the tags will
* hook into your ng-show/ng-hide, input, textarea, button, select and ng-required directives and adds the
* appropriate ARIA attributes.
* ## Usage
*
* Currently, the following ARIA attributes are implemented:
* For ngAria to do its magic, simply include the module as a dependency. The directives supported
* by ngAria are:
* `ngModel`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`, `ngDblClick`, and `ngMessages`.
*
* + aria-hidden
* + aria-checked
* + aria-disabled
* + aria-required
* + aria-invalid
* + aria-multiline
* + aria-valuenow
* + aria-valuemin
* + aria-valuemax
* + tabindex
* Below is a more detailed breakdown of the attributes handled by ngAria:
*
* You can disable individual ARIA attributes by using the {@link ngAria.$ariaProvider#config config} method.
* | Directive | Supported Attributes |
* |---------------------------------------------|----------------------------------------------------------------------------------------|
* | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required |
* | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled |
* | {@link ng.directive:ngShow ngShow} | aria-hidden |
* | {@link ng.directive:ngHide ngHide} | aria-hidden |
* | {@link ng.directive:ngClick ngClick} | tabindex |
* | {@link ng.directive:ngDblclick ngDblclick} | tabindex |
* | {@link module:ngMessages ngMessages} | aria-live |
*
* Find out more information about each directive by reading the
* {@link guide/accessibility ngAria Developer Guide}.
*
* ##Example
* Using ngDisabled with ngAria:
* ```html
* <md-checkbox ng-disabled="disabled">
* ```
* Becomes:
* ```html
* <md-checkbox ng-disabled="disabled" aria-disabled="true">
* ```
*
* ##Disabling Attributes
* It's possible to disable individual attributes added by ngAria with the
* {@link ngAria.$ariaProvider#config config} method. For more details, see the
* {@link guide/accessibility Developer Guide}.
*/
/* global -ngAriaModule */
var ngAriaModule = angular.module('ngAria', ['ng']).
provider('$aria', $AriaProvider);
@@ -42,10 +58,20 @@ var ngAriaModule = angular.module('ngAria', ['ng']).
*
* @description
*
* Used for configuring ARIA attributes.
* Used for configuring the ARIA attributes injected and managed by ngAria.
*
* ```js
* angular.module('myApp', ['ngAria'], function config($ariaProvider) {
* $ariaProvider.config({
* ariaValue: true,
* tabindex: false
* });
* });
*```
*
* ## Dependencies
* Requires the {@link ngAria} module to be installed.
*
*/
function $AriaProvider() {
var config = {
@@ -108,7 +134,41 @@ function $AriaProvider() {
*
* @description
*
* Contains helper methods for applying ARIA attributes to HTML
* The $aria service contains helper methods for applying common
* [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives.
*
* ngAria injects common accessibility attributes that tell assistive technologies when HTML
* elements are enabled, selected, hidden, and more. To see how this is performed with ngAria,
* let's review a code snippet from ngAria itself:
*
*```js
* ngAriaModule.directive('ngDisabled', ['$aria', function($aria) {
* return $aria.$$watchExpr('ngDisabled', 'aria-disabled');
* }])
*```
* Shown above, the ngAria module creates a directive with the same signature as the
* traditional `ng-disabled` directive. But this ngAria version is dedicated to
* solely managing accessibility attributes. The internal `$aria` service is used to watch the
* boolean attribute `ngDisabled`. If it has not been explicitly set by the developer,
* `aria-disabled` is injected as an attribute with its value synchronized to the value in
* `ngDisabled`.
*
* Because ngAria hooks into the `ng-disabled` directive, developers do not have to do
* anything to enable this feature. The `aria-disabled` attribute is automatically managed
* simply as a silent side-effect of using `ng-disabled` with the ngAria module.
*
* The full list of directives that interface with ngAria:
* * **ngModel**
* * **ngShow**
* * **ngHide**
* * **ngClick**
* * **ngDblclick**
* * **ngMessages**
* * **ngDisabled**
*
* Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each
* directive.
*
*
* ## Dependencies
* Requires the {@link ngAria} module to be installed.
@@ -238,5 +298,16 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
.directive('ngDisabled', ['$aria', function($aria) {
return $aria.$$watchExpr('ngDisabled', 'aria-disabled');
}])
.directive('ngMessages', function() {
return {
restrict: 'A',
require: '?ngMessages',
link: function(scope, elem, attr, ngMessages) {
if (!elem.attr('aria-live')) {
elem.attr('aria-live', 'assertive');
}
}
};
})
.directive('ngClick', ngAriaTabindex)
.directive('ngDblclick', ngAriaTabindex);
+3 -3
View File
@@ -40,9 +40,9 @@ $provide.value("$locale", {
"Sa"
],
"SHORTMONTH": [
"Jan",
"Feb",
"Mar",
"Jan.",
"Feb.",
"Mrt.",
"Apr",
"Mei",
"Jun",
+4 -4
View File
@@ -40,9 +40,9 @@ $provide.value("$locale", {
"Sa"
],
"SHORTMONTH": [
"Jan",
"Feb",
"Mar",
"Jan.",
"Feb.",
"Mrt.",
"Apr",
"Mei",
"Jun",
@@ -53,7 +53,7 @@ $provide.value("$locale", {
"Nov",
"Des"
],
"fullDate": "EEEE dd MMMM y",
"fullDate": "EEEE, dd MMMM y",
"longDate": "dd MMMM y",
"medium": "dd MMM y h:mm:ss a",
"mediumDate": "dd MMM y",
+4 -4
View File
@@ -40,9 +40,9 @@ $provide.value("$locale", {
"Sa"
],
"SHORTMONTH": [
"Jan",
"Feb",
"Mar",
"Jan.",
"Feb.",
"Mrt.",
"Apr",
"Mei",
"Jun",
@@ -53,7 +53,7 @@ $provide.value("$locale", {
"Nov",
"Des"
],
"fullDate": "EEEE dd MMMM y",
"fullDate": "EEEE, dd MMMM y",
"longDate": "dd MMMM y",
"medium": "dd MMM y h:mm:ss a",
"mediumDate": "dd MMM y",
+2 -2
View File
@@ -26,7 +26,7 @@ $provide.value("$locale", {
"\u1301\u120b\u12ed",
"\u12a6\u1308\u1235\u1275",
"\u1234\u1355\u1274\u121d\u1260\u122d",
"\u12a6\u12ad\u1270\u12cd\u1260\u122d",
"\u12a6\u12ad\u1276\u1260\u122d",
"\u1296\u126c\u121d\u1260\u122d",
"\u12f2\u1234\u121d\u1260\u122d"
],
@@ -49,7 +49,7 @@ $provide.value("$locale", {
"\u1301\u120b\u12ed",
"\u12a6\u1308\u1235",
"\u1234\u1355\u1274",
"\u12a6\u12ad\u1270",
"\u12a6\u12ad\u1276",
"\u1296\u126c\u121d",
"\u12f2\u1234\u121d"
],
+2 -2
View File
@@ -26,7 +26,7 @@ $provide.value("$locale", {
"\u1301\u120b\u12ed",
"\u12a6\u1308\u1235\u1275",
"\u1234\u1355\u1274\u121d\u1260\u122d",
"\u12a6\u12ad\u1270\u12cd\u1260\u122d",
"\u12a6\u12ad\u1276\u1260\u122d",
"\u1296\u126c\u121d\u1260\u122d",
"\u12f2\u1234\u121d\u1260\u122d"
],
@@ -49,7 +49,7 @@ $provide.value("$locale", {
"\u1301\u120b\u12ed",
"\u12a6\u1308\u1235",
"\u1234\u1355\u1274",
"\u12a6\u12ad\u1270",
"\u12a6\u12ad\u1276",
"\u1296\u126c\u121d",
"\u12f2\u1234\u121d"
],
+5 -5
View File
@@ -38,15 +38,15 @@ $provide.value("$locale", {
"de xineru",
"de febreru",
"de marzu",
"d'abril",
"d\u2019abril",
"de mayu",
"de xunu",
"de xunetu",
"d'agostu",
"d\u2019agostu",
"de setiembre",
"d'ochobre",
"d\u2019ochobre",
"de payares",
"d'avientu"
"d\u2019avientu"
],
"SHORTDAY": [
"dom",
@@ -71,7 +71,7 @@ $provide.value("$locale", {
"pay",
"avi"
],
"fullDate": "EEEE, dd MMMM 'de' y",
"fullDate": "EEEE, d MMMM 'de' y",
"longDate": "d MMMM 'de' y",
"medium": "d MMM y HH:mm:ss",
"mediumDate": "d MMM y",
+5 -5
View File
@@ -38,15 +38,15 @@ $provide.value("$locale", {
"de xineru",
"de febreru",
"de marzu",
"d'abril",
"d\u2019abril",
"de mayu",
"de xunu",
"de xunetu",
"d'agostu",
"d\u2019agostu",
"de setiembre",
"d'ochobre",
"d\u2019ochobre",
"de payares",
"d'avientu"
"d\u2019avientu"
],
"SHORTDAY": [
"dom",
@@ -71,7 +71,7 @@ $provide.value("$locale", {
"pay",
"avi"
],
"fullDate": "EEEE, dd MMMM 'de' y",
"fullDate": "EEEE, d MMMM 'de' y",
"longDate": "d MMMM 'de' y",
"medium": "d MMM y HH:mm:ss",
"mediumDate": "d MMM y",
+19 -19
View File
@@ -31,27 +31,27 @@ $provide.value("$locale", {
"\u0434\u0435\u043a\u0430\u0431\u0440"
],
"SHORTDAY": [
"B.",
"B.E.",
"\u00c7.A.",
"\u00c7.",
"C.A.",
"C",
"\u015e."
"\u0431\u0430\u0437\u0430\u0440",
"\u0431\u0430\u0437\u0430\u0440 \u0435\u0440\u0442\u04d9\u0441\u0438",
"\u0447\u04d9\u0440\u0448\u04d9\u043d\u0431\u04d9 \u0430\u0445\u0448\u0430\u043c\u044b",
"\u0447\u04d9\u0440\u0448\u04d9\u043d\u0431\u04d9",
"\u04b9\u04af\u043c\u04d9 \u0430\u0445\u0448\u0430\u043c\u044b",
"\u04b9\u04af\u043c\u04d9",
"\u0448\u04d9\u043d\u0431\u04d9"
],
"SHORTMONTH": [
"yan",
"fev",
"mar",
"apr",
"may",
"iyn",
"iyl",
"avq",
"sen",
"okt",
"noy",
"dek"
"\u0458\u0430\u043d\u0432\u0430\u0440",
"\u0444\u0435\u0432\u0440\u0430\u043b",
"\u043c\u0430\u0440\u0442",
"\u0430\u043f\u0440\u0435\u043b",
"\u043c\u0430\u0439",
"\u0438\u0458\u0443\u043d",
"\u0438\u0458\u0443\u043b",
"\u0430\u0432\u0433\u0443\u0441\u0442",
"\u0441\u0435\u043d\u0442\u0458\u0430\u0431\u0440",
"\u043e\u043a\u0442\u0458\u0430\u0431\u0440",
"\u043d\u043e\u0458\u0430\u0431\u0440",
"\u0434\u0435\u043a\u0430\u0431\u0440"
],
"fullDate": "EEEE, d, MMMM, y",
"longDate": "d MMMM, y",
+19 -19
View File
@@ -31,27 +31,27 @@ $provide.value("$locale", {
"\u0434\u0435\u043a\u0430\u0431\u0440"
],
"SHORTDAY": [
"B.",
"B.E.",
"\u00c7.A.",
"\u00c7.",
"C.A.",
"C",
"\u015e."
"\u0431\u0430\u0437\u0430\u0440",
"\u0431\u0430\u0437\u0430\u0440 \u0435\u0440\u0442\u04d9\u0441\u0438",
"\u0447\u04d9\u0440\u0448\u04d9\u043d\u0431\u04d9 \u0430\u0445\u0448\u0430\u043c\u044b",
"\u0447\u04d9\u0440\u0448\u04d9\u043d\u0431\u04d9",
"\u04b9\u04af\u043c\u04d9 \u0430\u0445\u0448\u0430\u043c\u044b",
"\u04b9\u04af\u043c\u04d9",
"\u0448\u04d9\u043d\u0431\u04d9"
],
"SHORTMONTH": [
"yan",
"fev",
"mar",
"apr",
"may",
"iyn",
"iyl",
"avq",
"sen",
"okt",
"noy",
"dek"
"\u0458\u0430\u043d\u0432\u0430\u0440",
"\u0444\u0435\u0432\u0440\u0430\u043b",
"\u043c\u0430\u0440\u0442",
"\u0430\u043f\u0440\u0435\u043b",
"\u043c\u0430\u0439",
"\u0438\u0458\u0443\u043d",
"\u0438\u0458\u0443\u043b",
"\u0430\u0432\u0433\u0443\u0441\u0442",
"\u0441\u0435\u043d\u0442\u0458\u0430\u0431\u0440",
"\u043e\u043a\u0442\u0458\u0430\u0431\u0440",
"\u043d\u043e\u0458\u0430\u0431\u0440",
"\u0434\u0435\u043a\u0430\u0431\u0440"
],
"fullDate": "EEEE, d, MMMM, y",
"longDate": "d MMMM, y",
+1 -1
View File
@@ -36,7 +36,7 @@ $provide.value("$locale", {
"\u00c7.A.",
"\u00c7.",
"C.A.",
"C",
"C.",
"\u015e."
],
"SHORTMONTH": [
+1 -1
View File
@@ -36,7 +36,7 @@ $provide.value("$locale", {
"\u00c7.A.",
"\u00c7.",
"C.A.",
"C",
"C.",
"\u015e."
],
"SHORTMONTH": [
+1 -1
View File
@@ -36,7 +36,7 @@ $provide.value("$locale", {
"\u00c7.A.",
"\u00c7.",
"C.A.",
"C",
"C.",
"\u015e."
],
"SHORTMONTH": [
+2 -2
View File
@@ -58,8 +58,8 @@ $provide.value("$locale", {
"medium": "d.MM.y '\u0433'. H:mm:ss",
"mediumDate": "d.MM.y '\u0433'.",
"mediumTime": "H:mm:ss",
"short": "d.MM.yy H:mm",
"shortDate": "d.MM.yy",
"short": "d.MM.yy '\u0433'. H:mm",
"shortDate": "d.MM.yy '\u0433'.",
"shortTime": "H:mm"
},
"NUMBER_FORMATS": {

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