Compare commits

..

74 Commits

Author SHA1 Message Date
Brian Ford 00456a8f93 docs(changelog): release notes for 1.2.23 superficial-malady 2014-08-22 15:56:49 -07:00
Brian Ford 0b3022e8e0 docs(changelog): release notes for 1.3.0-beta.19 rafter-ascension 2014-08-22 15:56:33 -07:00
Jeff Cross 456026eff1 fix(input): use lowercase method to account for undefined type 2014-08-22 00:41:02 -07:00
danrbergman 8ec3efd967 docs(guide/module): update tag in description
the reference to 'myApp' module changed in the example from <html> to a <div>. Updating description
to reflect the new <div> tag.

Closes #8720
2014-08-21 23:33:59 -04:00
danrbergman 8528781f85 docs(guide/module): make the use of ng-app explicit in example
Helpful for people new to Angular to see the ng-app declaration in context with the expression
example. This will help illustrate the "Important thing to notice" point which follows: "The
reference to myApp module in <html ng-app="myApp">. This is what bootstraps the app using your
module."

Closes #8673
2014-08-21 22:28:48 -04:00
Sekib Omazic 98f603722d fix(Angular): make Date comparison in equals() NaN-aware
Make angular.equals() Date comparison NaN-aware to prevent infinite digest errors when a dealy watched
date has an invalid value.

Closes #8650
Closes #8715
2014-08-21 21:17:46 -04:00
Caitlin Potter ebece0bcb9 fix(input): by default, do not trim input[type=password] values
Do not trim input[type=password] values

BREAKING CHANGE:

Previously, input[type=password] would trim values by default, and would require an explicit ng-trim="false"
to disable the trimming behaviour. After this CL, ng-trim no longer effects input[type=password], and will
never trim the password value.

Closes #8250
Closes #8230

Conflicts:
	src/ng/directive/input.js
2014-08-21 19:14:27 -04:00
Caitlin Potter 4b7398eeca chore(protractor): enable testing ng-app-included examples
/cc @petebacondarwin / @juliemr please review :>

Blocks #8673
Closes #8677
2014-08-21 18:55:12 -04:00
Casey Flynn 4e79decc30 docs($interval): fix typo in example
It's not "nis", it's "is"! 〜( ̄▽ ̄)〜

Closes #8711
2014-08-21 15:30:16 -04:00
Caitlin Potter 888b0f5400 fix(copy): clear array destinations correctly for non-array sources
Closes #8610
Closes #8702
2014-08-20 21:51:32 -04:00
Jeff Cross aaf9c5e598 fix(minErr): encode btstrpd error input to strip angle brackets
The $sanitize service was returning an empty string to the error page
because the input was usually a single html tag (sometimes it could be
`document`). This fix replaces angle brackets with html entities.

Closes #8683
2014-08-20 17:27:48 -07:00
Henrik Nyh 893d2f8000 docs(ngDisabled): clarify "don't do this" example
It's not clear until you read the whole thing that it's an explanation
of what *not* to do and why, so if you scan the page from the top, you
may use this bad solution.
2014-08-20 15:47:37 -07:00
Brian Ford effc98fdc9 fix(linky): handle quotes around email addresses
Closes #8520
2014-08-20 13:40:31 -07:00
mishoo78 64cdbf3ae9 docs(ngMock): note that inject/module helpers only defined for jasmine / mocha
Closes #8694
2014-08-20 13:44:24 -04:00
Ole Weitz 5bba892ffd docs($cacheFactory): prevent example breaking on key update
The example for $cacheFactory breaks when a user tries to update a value for a key.
Setting a new value for an existing key results in duplicate key entries in the key array, thus
breaking the ng-repeat directive. With this fix the key is only added if it isn't contained in the
cache.

Closes #8214
2014-08-20 10:49:14 -04:00
Izhaki ec27deacfd docs($compile): fix documentation for ?^ controller search
Fixed typo: 'parents parents' to 'parents'

Closes #8690
2014-08-20 09:32:20 -04:00
Caitlin Potter c6e4defcb6 fix($location): rewrite relative URI correctly if path==='/' in legacy html5Mode
Currently, legacy browsers get to use a clever scheme for resolving relative URIs in html5Mode,
and resolve the URI relative to $location.path().

Currently, $location.path() can be '/' under certain circumstances, which means that when we
split $location.path() on '/' and later join by '/' after adding another path component,
we end up with '//pathComponent'. $$rewrite fails to deal with this correctly, and effectively
the $location is never changed from the root path.

This CL corrects this by ensuring that the duplicate '/' situation does not occur when resolving
relative URIs.

Closes #8684
2014-08-19 21:31:20 -04:00
Caitlin Potter 74a7afcb31 fix($location): don't call indexOf() of undefined href attribute
Closes #7721
Closes #8681
2014-08-19 19:16:40 -04:00
Brian Ford 08cc6edd38 chore(jshint): add missing scripturl directive 2014-08-19 14:56:06 -07:00
Brian Ford 4f3870500d fix($sanitize): sanitize javascript urls with comments
Closes #8274
2014-08-19 14:16:01 -07:00
Marty Kane cd0507bc3a docs(guide/di): correct a few awkward sentences
Closes #8678
2014-08-19 16:51:45 -04:00
Izhaki 20eef05195 docs($compile): correct documentation for directive controller ^ notation
`^` searches the element and its parents, not exclusively the element's parents. This confuses
a lot of people :(

Closes #8622
2014-08-19 14:31:36 -04:00
Shahar Talmi a6d7b4bdbd docs(*): use @description instead of @returns for properties
Dgeni-packages was not actually rendering the `@returns` text.

Closes #8639
2014-08-19 14:26:06 -04:00
Baptiste Fontaine 5811c5c35b docs(ngBind): irrelevant text removed from ngBindHtml’s example
The ngBindHtml’s example had a copied line from ngBindTemplate’s that’s irrelevant here.

Closes #8668
2014-08-19 12:39:04 -04:00
Pawel Kozlowski e37e30e93f docs(orderBy): clarify expression usage in a predicate
Closes #8592
2014-08-18 13:32:21 -07:00
Tom Kadwill a0b8ab8d7d docs(tutorial/index): improve wording
Removed repetition of 'machine' and 'local machine'.
I think this change makes the sentence more concise
2014-08-18 13:23:56 -07:00
Jeff Sheets 129c53730c docs($http): correct link to "salt (cryptography)" wikipedia article
Closes #8654
2014-08-18 13:35:24 -04:00
Caitlin Potter 8695138aec docs(misc/contribute): fix syntax highlighting of URLS
Closes #8168
Closes #8169
2014-08-17 21:13:57 -04:00
Shahar Talmi ed56872bb2 fix(ngHref): remove attribute when empty value instead of ignoring
Closes #2755
2014-08-13 15:43:10 -07:00
Peter Bacon Darwin cb183433a0 chore(doc-gen): move e2e tests into docs folder
These tests didn't really fit in the test folder as the docs app is mostly
a separate entity from the AngularJS codebase.
2014-08-13 11:24:53 +01:00
Caitlin Potter 631fbda6a9 docs(CHANGELOG.md): fix typo, it's 1.2.22 not 1.2.2!
Whoops!
2014-08-12 16:53:05 -04:00
Caitlin Potter 4c33b56612 docs(CHANGELOG.md): add changelog for v1.3.0-beta.18 and v1.2.22
Closes #8581
2014-08-12 13:17:04 -04:00
rodyhaddad 93b0c2d892 feat($parse): allow for assignments in ternary operator branches
Closes #8512
Closes #8484
CLoses #5434

Conflicts:
	test/ng/parseSpec.js
2014-08-11 17:04:40 +01:00
Peter Bacon Darwin d262378b7c fix(jqLite): allow triggerHandler() to accept custom event
In some scenarios you want to be able to specify properties on the event
that is passed to the event handler. JQuery does this by overloading the
first parameter (`eventName`). If it is an object with a `type` property
then we assume that it must be a custom event.

In this case the custom event must provide the `type` property which is
the name of the event to be triggered.  `triggerHandler` will continue to
provide dummy default functions for `preventDefault()`, `isDefaultPrevented()`
and `stopPropagation()` but you may override these with your own versions
in your custom object if you wish.

In addition the commit provides some performance and memory usage
improvements by only creating objects and doing work that is necessary.

This commit also renames the parameters inline with jQuery.

Closes #8469
Closes #8505
2014-08-11 12:17:55 +01:00
Peter Bacon Darwin 7729c84ec7 test(docsAppE2E): check that param defaults are shown in docs
Closes https://github.com/angular/dgeni-packages/pull/58
2014-08-10 20:19:23 +01:00
Peter Bacon Darwin dffeef29d7 test(docsAppE2E): tighten CSS selector to only find one element 2014-08-10 20:19:23 +01:00
Peter Bacon Darwin 2e22588ccf chore(package.json): update to dgeni-packages 0.9.7 2014-08-10 17:43:23 +01:00
Caitlin Potter 0b0acb0342 fix($compile): make '='-bindings NaN-aware
Update parent and child scopes correctly when a '='-binding changes from a NaN value.

TBR by angular-core

Closes #8553
Closes #8554

Conflicts:
	test/ng/compileSpec.js
2014-08-10 03:32:13 -04:00
Eddie Hedges 18fc43e828 docs(guide): correct links to unit testing guides
Closes #8548
2014-08-09 14:01:54 -04:00
Caitlin Potter ed47d811e2 chore(travis): specify chrome m34 in protractor configuration
Closes #8541
2014-08-08 17:21:00 -04:00
Derrick Mar a84f9f6178 docs(tutorial/step-10): add mock image data to spec
Closes #8468
Closes #8535
2014-08-08 20:37:42 +01:00
Caitlin Potter 4fbbe1152e docs(http): don't use locale-specific uri for MDN link 2014-08-08 15:32:38 -04:00
Joey Yang df3d941c57 docs($http): fix broken markdown link in withCredentials description
Markdown typo in $http config documentation

Closes #7859
2014-08-08 15:29:50 -04:00
Juampy 2bd3214a55 docs(guide/migration): ngSanitize is out ng core at AngularJS 1.2.21
When using ngBindHTML directive, either ngSanitize or $sce must be used
or this will end in a non-trusted value error.

Closes #8519
2014-08-07 21:04:17 -04:00
James Kleeh fc2abef327 docs(guide/directive): explain how to require multiple controllers
Closes #8524
2014-08-07 15:14:39 +01:00
Andrew Silluron 76a0eb89fb docs($compile): fix typo 'default' spelling
Change spelling of 'defualt' to 'default'

Closes #8476
2014-08-04 18:48:29 -04:00
Caitlin Potter ee57b4c26b docs(CHANGELOG.md): add missing breaking change from 1.3.0-beta.14 2014-08-04 17:29:33 -04:00
Peter Bacon Darwin 29eaabc000 test(select): relax test for IE8 bug
There is a bug in IE8 (http://support.microsoft.com/kb/829907 and
http://yuilibrary.com/forum-archive/forum/viewtopic.php@p=14826.html):
when you clone an `<option>` element the selected attribute on the options
can become invalid.

This is not relevant to the proper behaviour of the `select` directive
since it uses `prop` not `attr` to store the selected status of each
option.

This test is only interested in there being at least on option with
the `selected` attribute, for conformance to accessibility guidelines.
So we can safely relax the test to check this rather than concerning
ourselves with which option actually has this attribute.

Fixes 79538afd7b
Closes #8465
2014-08-03 22:15:59 +01:00
Joseph Spencer 2a6081057f docs($resource): clarify the meaning of @ in paramDefaults
Closes #8457
2014-08-03 17:15:03 +01:00
Peter Bacon Darwin 79538afd7b fix(select): ensure that at least one option has the selected attribute set
Using `prop` to set selected is correct programmatically but accessibility
guidelines suggest that at least on item should have the `selected` attribute
set.

Closes #8366
Closes #8429

Conflicts:
	test/ng/directive/selectSpec.js
2014-08-03 16:37:32 +01:00
rodyhaddad bcfa64e77c chore(travis): rename fetch_bundle script and make it not abort a travis build if it fails
This is useful when the npm-bundle-deps server isn't running,
when the tar never gets served (there's a default timeout on the request),
or when the served file isn't a valid tar.
2014-08-01 19:14:48 -07:00
Peter Bacon Darwin b7a2deed30 docs(ngSubmit): add link to form docs to discourage double submission
Closes #6017
2014-07-31 21:27:04 +01:00
Ken Sheedlo 98ff901bda docs(error/$injector/unpr): inadvertently redefining a module can cause error
Closes #8421
2014-07-31 20:48:32 +01:00
k-funk 428b81cba9 docs($http): add link to $http.path()
Closes #8424
2014-07-31 13:43:48 +01:00
Danielle 5dae9c230e docs(ngMockE2E): remove repeated word
Closes #8411
2014-07-31 13:41:05 +01:00
winsontam c3fad1157e fix($location) don't rewrite location when clicking on "javascript:" or "mailto:" link
Previously, absent a specified target attribute, when clicking on an anchor tag with an href beginning
with either "javascript:" or "mailto:", the framework would rewrite the URL, when it ought not to.

With this change, the browser is prevented from rewriting if the URL begins with a case-insensitive match
for "javascript:" or "mailto:", optionally preceeded by whitespace.

Closes #8407
Closes #8425
Closes #8426
2014-07-31 07:31:33 -04:00
Peter Bacon Darwin 9242c580a1 refact(select): don't recreate selectedSet on every digest loop
In the case of a "multiple" select, the model value is an array, changes
to which don't get picked up by NgModelController as it only looks for
object identity change.

We were rebuilding the `selectedSet` (a hash map of selected items) from
the modelValue on every turn of the digest. This is not needed as we can
simply use `$watchCollection` directly on the `$modelValue` instead.
2014-07-31 07:14:14 +01:00
Peter Bacon Darwin c2860944c6 fix(select): do not update selected property of an option element on digest with no change event
The `render()` method was being invoked on every turn of the digest cycle,
which was inadvertently updating the DOM even when a `change` event had
not been triggered.

This change only calls the `render()` method when `ctrl.$render()` is called,
as part of the NgModelController` lifecycle and when the `modelValue` has
significantly changed.

Closes #8221
Closes #7715
2014-07-30 23:14:40 +01:00
Peter Bacon Darwin e9a00fbdc3 test(select): add extra expectations and comments for clarity 2014-07-30 23:14:40 +01:00
Erin Altenhof-Long d018ac2a9a test(select): add test of updating the model because of ng-change
A regression #7855 was introduced by
https://github.com/angular/angular.js/commit/dc149de9364c66b988f169f67cad39577ba43434
This test ensures that reverting that commit fixes this regression.
In the regression, changes to a bound model in ng-change were not propagated back to the view.

Test for #7855
2014-07-30 23:14:40 +01:00
Erin Altenhof-Long b770c353bc test(select): add test cases for selects with blank disabled options
An earlier commit dc149de936 caused an error where the first option of
a select would be skipped over if it had a blank disabled value. These tests demonstrate that with
that commit in place, blank disabled options are skipped in a select. When the commit is reverted,
the correct behavior is seen that the blank disabled option is still selected in both selects
marked with required and those that have optional choices.

Relates to #7715
2014-07-30 23:14:40 +01:00
Erin Altenhof-Long 812277c257 test(select): add test against updating selected property on digest with no change event
Commit dc149de936 was reverted to fix regressions #7715 and #7855.
This commit introduced this test case and a corresponding fix for preventing the update of the
selected property of an option element on a digest with no change event. Although the previous fix
introduced regressions, the test covers a valid issue and should be included.
2014-07-30 23:14:39 +01:00
Erin Altenhof-Long 61871da9de revert(select): avoid checking option element selected properties in render
This reverts commit dc149de936. That commit fixes a bug caused by
Firefox updating `select.value` on hover. However, it
causes other bugs with select including the issue described in #7715. This issue details how
selects with a blank disabled option skip to the second option. We filed a bug
with Firefox for the problematic behavior the reverted commit addresses
https://bugzilla.mozilla.org/show_bug.cgi?id=1039047, and alternate Angular fixes are being
investigated.

Closes #7715 #7855
2014-07-30 23:14:39 +01:00
Caitlin Potter 9ee075518f fix(ngSanitize): ensure html is a string in htmlParser()
Previously, $sanitize(nonString) would throw. Now, the type is converted to a string before any work
is done.

Closes #8417
Closes #8416
2014-07-30 13:34:14 -04:00
Misha Moroshko afe93eaff8 docs(dateFilter): fix milliseconds example
Closes #8389
2014-07-29 17:49:28 +01:00
Andrew Pham d8f94d1a3f docs(tutorial/step-2): warn reader not to minimise browser that Karma's running on
Closes #8386
2014-07-29 17:40:46 +01:00
Nathan Wiebe f53b53df22 docs(tutorial): clarify sentence in step 02
Separate two sentences with a period, and clarify the wording by making it less technical: "to the DOM"
vs "to the <body> tag".

Closes #8394
2014-07-29 11:42:15 -04:00
Shahar Talmi eab5731afc feat(http): allow caching for JSONP requests
Closes #1947
Closes #8356
2014-07-29 09:40:36 +01:00
Sergio Sanguanini 986c446aaf style(AngularPublic): add whitespace to jshint block
Closes #8360
2014-07-27 14:10:40 +01:00
rodyhaddad 60366c8d0b fix($parse): correctly assign expressions who's path is undefined and that use brackets notation
Closes #8039
2014-07-26 23:28:20 -07:00
Julie Ralph 494c8aa0b3 chore(ci): update sauce connect from 3 to 4.3 for the 1.2.x branch 2014-07-25 14:50:24 -07:00
Ivan Alvarez cd9459e129 docs(tutorial): update step_03.ngdoc
1) The original document is not clear to a new developer in where to place the code.
2) The query.clear() statement to clear the query before the second test is missing in the original document.
3) Refactored to use the query and phoneList variables in both tests, so its easier to read and understand.

Closes #7815
2014-07-25 14:26:49 -07:00
Mitch Robb 2862883bd8 docs(tutorial): update step7 ngdoc to fix grammar
This line was missing an 'as'

Previous:
We also have to add the modules dependencies of our app. By listing these two modules as dependencies of `phonecatApp`, ...

New:
We also have to add the modules *as* dependencies of our app.

Closes #8345
2014-07-25 14:12:59 -07:00
Jeff Cross bbb673a48a docs(changelog): release notes for 1.3.0-beta.17 and 1.2.21 2014-07-25 10:17:16 -07:00
60 changed files with 1280 additions and 131 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ install:
- npm config set spin false
# Log HTTP requests
- npm config set loglevel http
- time ./scripts/travis/fetch_bundle.sh
- time ./scripts/travis/npm-bundle-deps.sh
- time npm install
before_script:
+575
View File
@@ -1,3 +1,548 @@
<a name="1.2.23"></a>
# 1.2.23 superficial-malady (2014-08-22)
## Bug Fixes
- **$location:**
- rewrite relative URI correctly if `path==='/'` in legacy html5Mode
([c6e4defc](https://github.com/angular/angular.js/commit/c6e4defcb6ec1ff43e9590b8fe9601d9e9da445d),
[#8684](https://github.com/angular/angular.js/issues/8684))
- don't call `indexOf()` of undefined `href` attribute
([74a7afcb](https://github.com/angular/angular.js/commit/74a7afcb31b2e2aef2d7a4c3e3cf29f320669b0e),
[#7721](https://github.com/angular/angular.js/issues/7721), [#8681](https://github.com/angular/angular.js/issues/8681))
- **$sanitize:** sanitize javascript urls with comments
([4f387050](https://github.com/angular/angular.js/commit/4f3870500da6f6f0c1b1d20c70404996b1a39585),
[#8274](https://github.com/angular/angular.js/issues/8274))
- **Angular:** make Date comparison in equals() NaN-aware
([98f60372](https://github.com/angular/angular.js/commit/98f603722d81046031ad4a10e0a49b692871c2b2),
[#8650](https://github.com/angular/angular.js/issues/8650), [#8715](https://github.com/angular/angular.js/issues/8715))
- **copy:** clear array destinations correctly for non-array sources
([888b0f54](https://github.com/angular/angular.js/commit/888b0f5400c2357dcc91300d1a4e66e52a8d8801),
[#8610](https://github.com/angular/angular.js/issues/8610), [#8702](https://github.com/angular/angular.js/issues/8702))
- **input:**
- use lowercase method to account for undefined type
([456026ef](https://github.com/angular/angular.js/commit/456026eff12ad70fa27dd08ec6bddc63e0f3e604))
- by default, do not trim `input[type=password]` values
([ebece0bc](https://github.com/angular/angular.js/commit/ebece0bcb9d64e59beb1c9b3418bed25e50ceef4),
[#8250](https://github.com/angular/angular.js/issues/8250), [#8230](https://github.com/angular/angular.js/issues/8230))
- **linky:** handle quotes around email addresses
([effc98fd](https://github.com/angular/angular.js/commit/effc98fdc91937ae0aca30bc53e34a3c29863cd6),
[#8520](https://github.com/angular/angular.js/issues/8520))
- **minErr:** encode btstrpd error input to strip angle brackets
([aaf9c5e5](https://github.com/angular/angular.js/commit/aaf9c5e598996ab17bce9579c8bfe63628b6620e),
[#8683](https://github.com/angular/angular.js/issues/8683))
- **ngHref:** remove attribute when empty value instead of ignoring
([ed56872b](https://github.com/angular/angular.js/commit/ed56872bb2c9c479f90a479f52e3d4ef9c80d0c7),
[#2755](https://github.com/angular/angular.js/issues/2755))
## Breaking Changes
- **input:** due to [ebece0bc](https://github.com/angular/angular.js/commit/ebece0bcb9d64e59beb1c9b3418bed25e50ceef4),
Previously, `input[type=password]` would trim values by default, and would require an explicit ng-trim="false"
to disable the trimming behaviour. After this CL, `ng-trim` no longer affects `input[type=password]`, and will
never trim the password value.
Closes #8250
Closes #8230
<a name="1.3.0-beta.19"></a>
# 1.3.0-beta.19 rafter-ascension (2014-08-22)
## Bug Fixes
- **$compile:**
- use the correct namespace for transcluded SVG elements
([ffbd276d](https://github.com/angular/angular.js/commit/ffbd276d6def6ff35bfdb30553346e985f4a0de6),
[#8716](https://github.com/angular/angular.js/issues/8716))
- update the jQuery `.context` when an element is replaced by `replace:true` directive
([f02f7d9c](https://github.com/angular/angular.js/commit/f02f7d9c15deea9c5d83212301e2a5e18223bbe5),
[#8253](https://github.com/angular/angular.js/issues/8253), [#7900](https://github.com/angular/angular.js/issues/7900))
- **$location:**
- rewrite relative URI correctly if `path==='/'` in legacy html5Mode
([d18b2819](https://github.com/angular/angular.js/commit/d18b2819768e467897dee7bc223876ca23ea71b1),
[#8684](https://github.com/angular/angular.js/issues/8684))
- don't call `indexOf()` of undefined `href` attribute
([5b77e30c](https://github.com/angular/angular.js/commit/5b77e30c1ac49be7b079b82527a5631f68bac904),
[#7721](https://github.com/angular/angular.js/issues/7721), [#8681](https://github.com/angular/angular.js/issues/8681))
- **$parse:** remove unused variable declaration in generated getters
([6acea115](https://github.com/angular/angular.js/commit/6acea1152f72a4026583897c67bea2839bc9e89e))
- **$sanitize:** sanitize javascript urls with comments
([b7e82a33](https://github.com/angular/angular.js/commit/b7e82a33eee03fc683f982c6ee13d15d88b07f67),
[#8274](https://github.com/angular/angular.js/issues/8274))
- **$watchGroup:** call listener once when the `watchExpressions` array is empty
([bf0e8373](https://github.com/angular/angular.js/commit/bf0e83732aa02c7aa08d0ccdf122116235fcfa11))
- **Angular:** make Date comparison in `equals()` `NaN`-aware
([693e846a](https://github.com/angular/angular.js/commit/693e846add5089d0e516604ae4a109e445fd3664),
[#8650](https://github.com/angular/angular.js/issues/8650), [#8715](https://github.com/angular/angular.js/issues/8715))
- **Scope:** don't clear the phase when an exception is thrown from asyncQueue or watch
([bf1a57ad](https://github.com/angular/angular.js/commit/bf1a57ad4822bb152fdd4d2fb54c0689e466481b))
- **copy:** clear array destinations correctly for non-array sources
([a603e202](https://github.com/angular/angular.js/commit/a603e202cc7e048c2ab6f12dee1cc8f277cf6f4f),
[#8610](https://github.com/angular/angular.js/issues/8610), [#8702](https://github.com/angular/angular.js/issues/8702))
- **forEach:** match behaviour of Array.prototype.forEach (ignore missing properties)
([36230194](https://github.com/angular/angular.js/commit/36230194be8aa417b0af33d618060829a75c4c5f),
[#8510](https://github.com/angular/angular.js/issues/8510), [#8522](https://github.com/angular/angular.js/issues/8522), [#8525](https://github.com/angular/angular.js/issues/8525))
- **input:**
- use lowercase method to account for undefined type
([066c0499](https://github.com/angular/angular.js/commit/066c049957a8af2fe449040eca2f1cb499655e32))
- by default, do not trim input[type=password] values
([a7fb357f](https://github.com/angular/angular.js/commit/a7fb357fa122e0a056ce1de838a2dfaf1ebc2953),
[#8250](https://github.com/angular/angular.js/issues/8250), [#8230](https://github.com/angular/angular.js/issues/8230))
- **jQuery:** cooperate with other libraries monkey-patching jQuery.cleanData
([b9389b26](https://github.com/angular/angular.js/commit/b9389b26ba2cf6aa70372fa32a7b28c62d174bf5),
[#8471](https://github.com/angular/angular.js/issues/8471))
- **jqLite:**
- clone wrapNode in jqlite/wrap
([77d3e754](https://github.com/angular/angular.js/commit/77d3e7544642396d868aa49b85f0c027e8057bd7),
[#3860](https://github.com/angular/angular.js/issues/3860), [#4194](https://github.com/angular/angular.js/issues/4194))
- revert the `ready()` optimization until jQuery does the same
([1bdca93d](https://github.com/angular/angular.js/commit/1bdca93d708ce9441b26d00e564210755395edf7))
- **linky:** handle quotes around email addresses
([a9d22712](https://github.com/angular/angular.js/commit/a9d227120dc2d433372da415a450e56b783b57a0),
[#8520](https://github.com/angular/angular.js/issues/8520))
- **minErr:** encode btstrpd error input to strip angle brackets
([0872388a](https://github.com/angular/angular.js/commit/0872388a1b88b8637fdb0fb1ebbee269bead0508),
[#8683](https://github.com/angular/angular.js/issues/8683))
- **ngRepeat:**
- allow aliasAs identifiers which contain but do not match reserved words
([d713ad1b](https://github.com/angular/angular.js/commit/d713ad1b6607389649fbb8d12ac103565b02a1d4),
[#8729](https://github.com/angular/angular.js/issues/8729))
- make allowed aliasAs expressions more strict
([09b29870](https://github.com/angular/angular.js/commit/09b298705f74255aff55bb7e4ba200c4200d712d),
[#8438](https://github.com/angular/angular.js/issues/8438), [#8440](https://github.com/angular/angular.js/issues/8440))
## Features
- **$compile:**
- use allOrNothing interpolation for ngAttr*
([09de7b5d](https://github.com/angular/angular.js/commit/09de7b5db466498becb295ecf5c1d0a698b1512c),
[#8376](https://github.com/angular/angular.js/issues/8376), [#8399](https://github.com/angular/angular.js/issues/8399))
- **benchpress:** configure benchpress grunt task
([6bdaa4bc](https://github.com/angular/angular.js/commit/6bdaa4bc213805a58f51e9f5285dfe03bb06ddc3))
- **jqLite:** implement the `detach` method
([1a05daf5](https://github.com/angular/angular.js/commit/1a05daf5dc67813528afdb88086766dc22b6c0df),
[#5461](https://github.com/angular/angular.js/issues/5461))
- **ngRoute:** add method for changing url params
([77a1acc7](https://github.com/angular/angular.js/commit/77a1acc7fcad7a8a7d0376b33d38a8977372cfe2))
## Performance Improvements
- **$compile:**
- don't register $destroy callbacks on element-transcluded nodes
([b5f7970b](https://github.com/angular/angular.js/commit/b5f7970be5950580bde4de0002a578daf3ae3aac))
- refactor publicLinkFn to simplify the code and use 'for in' loop
([645625cf](https://github.com/angular/angular.js/commit/645625cf349a4be57691a7bf418b2386b4c1a53d))
- clone the nodeList during linking only if necessary
([3e0a2e1f](https://github.com/angular/angular.js/commit/3e0a2e1f3367a5b4ae7d8de6cff559f522aacfba))
- delay object initialization in nodeLinkFn
([31ed0af7](https://github.com/angular/angular.js/commit/31ed0af74b0081906415dcefe5610e1217cc0c48))
- optimize nodeLinkFn
([35134a0e](https://github.com/angular/angular.js/commit/35134a0e237d193cd7d3995dacfdc6bf3e92635e))
- optimize publicLinkFn
([274e9c4d](https://github.com/angular/angular.js/commit/274e9c4ddfd64138d39fcf84047aabc3ccde2f0b))
- **$interpolate:** do not keep empty separators
([94b5c9f0](https://github.com/angular/angular.js/commit/94b5c9f00edff7fa631d09316ceb9c7fd4c6426a))
- **$parse:**
- don't bind filters to a context
([8863b9d0](https://github.com/angular/angular.js/commit/8863b9d04c722b278fa93c5d66ad1e578ad6eb1f))
- optimize filter implementation
([ece6ef47](https://github.com/angular/angular.js/commit/ece6ef479c741f17fc217d743cad64c516dbed27))
- speed up fn invocation for no args case
([a17578ad](https://github.com/angular/angular.js/commit/a17578ad3db5d1375aec1d601055ab718eeafd10))
- speed up fn invocation by optimizing arg collection
([fecfc5b0](https://github.com/angular/angular.js/commit/fecfc5b09feb7e4079364013b0beb6bf204ade2a))
- use no-proto maps as caches and avoid hasOwnProperty checks
([d302ea0c](https://github.com/angular/angular.js/commit/d302ea0cfade2787d7cc500398b7dcd3e4eff945))
- trim expression only if string
([a1341223](https://github.com/angular/angular.js/commit/a1341223c084c8188671bb8d6ea1608490b66f9f))
- **$rootScope:** do not use `Function::call` when not needed
([7eae29e5](https://github.com/angular/angular.js/commit/7eae29e5ab478ccb7e02fee8311f8b99ea1d165d))
- **Scope:**
- optimize `$watchCollection` when used for watching objects
([e822e906](https://github.com/angular/angular.js/commit/e822e9061c2a605649d91abbd641f757e2829275))
- don't use forEach in
([301463a2](https://github.com/angular/angular.js/commit/301463a2e249011d7cb696c6cf34254f8317a706))
- watchCollection optimization
([7d96ab0d](https://github.com/angular/angular.js/commit/7d96ab0d132d923ec3e3a212aaf9d79f1d4a02de))
- exit $broadcast early if nobody is listening for the given event
([a09fa356](https://github.com/angular/angular.js/commit/a09fa356416c033a52666f3becf00524ecff3a03))
- use remove the need for the extra watch in $watchGroup
([3f0e642e](https://github.com/angular/angular.js/commit/3f0e642eefcbbb315839c4456ba6ac029a7b8a20),
[#8396](https://github.com/angular/angular.js/issues/8396))
- **benchpress:** add benchpress node module and port over large table test
([1229334f](https://github.com/angular/angular.js/commit/1229334fbd8c778e95785d6a5e5589099ce655f7))
- **isObject:** use strict comparison
([d208ba25](https://github.com/angular/angular.js/commit/d208ba254442649d35f96c76bcd9e47326ec59f3))
- **jqLite:**
- simplify jqLiteDealoc
([f8f7a1df](https://github.com/angular/angular.js/commit/f8f7a1df34560222cb5d2e18d4be996f5553815a))
- optimize event handler
([d05f27e2](https://github.com/angular/angular.js/commit/d05f27e274c41c33eebf4fe8035715d3f6596069))
- only take `str.split()` path when needed
([187b1b8e](https://github.com/angular/angular.js/commit/187b1b8ef45babd86afa853dc9321cd23160096e),
[#8648](https://github.com/angular/angular.js/issues/8648))
- optimize `off()`
([abb17cce](https://github.com/angular/angular.js/commit/abb17cce8b459e4646d1c2a2428b691c3d95fb4c))
- refactor jqLiteExpandoStore to minimize access to expensive element.ng339 expando property
([1e8698b3](https://github.com/angular/angular.js/commit/1e8698b33e61b1a196f05f42856a2da4590a10e1))
- microoptimization in chaining fn
([fafbd494](https://github.com/angular/angular.js/commit/fafbd494907a8c068d79415b7ba8f42f283be521))
- don't use String#split in on() unless we need it
([bda673f8](https://github.com/angular/angular.js/commit/bda673f8e785f299407c8c45887f37448a0f0192))
- don't check isString many times in constructor
([443b521e](https://github.com/angular/angular.js/commit/443b521e22f9ec7009b913a2fe78caee0a515e87))
- optimize jqLiteAcceptsData method
([b493c62f](https://github.com/angular/angular.js/commit/b493c62f6b3e4288f5dee7c8b5952e088c2e3329))
- optimize `append()` and `after()`
([8d933bf9](https://github.com/angular/angular.js/commit/8d933bf99520fe3936e33d3ee28fd37e574b99de))
- don't register DOM listener for $destroy event
([6251751a](https://github.com/angular/angular.js/commit/6251751ad7bc2f3621db538edb5a9d7313a4ce6d))
- optimize event listener registration
([566f1015](https://github.com/angular/angular.js/commit/566f1015d27118d259e0886910d6b73b3cb0eb10))
- improve createEventHandler method by switching from forEach to for loop
([e9cd6dc0](https://github.com/angular/angular.js/commit/e9cd6dc055cb7bd80ae9232d8985b2bc3999135e))
- don't use `forEach` in `off()`
([960a8410](https://github.com/angular/angular.js/commit/960a8410515b2d7d461d7c95e8a2ca3d75129087))
- don't recreate the Node.contains polyfill
([d1536e7c](https://github.com/angular/angular.js/commit/d1536e7c8bf60549096138d08953a43190c7b1a6))
- speed up shallowCopy and special case Attributes cloning
([54fa16e4](https://github.com/angular/angular.js/commit/54fa16e45d8769ce6708a28388326db0eea53c7e))
- **ngBind:** bypass jquery/jqlite when setting text
([0a738ce1](https://github.com/angular/angular.js/commit/0a738ce1760f38efe45e79aa133442be09b56803))
- **ngRepeat:**
- simplify code and remove duplicate array.length access
([08eb0558](https://github.com/angular/angular.js/commit/08eb05583bf39c63fef43b4faf29c61360699c81))
- optimize marking of nodes that are being removed via an animation
([36e35b2c](https://github.com/angular/angular.js/commit/36e35b2cb17c5ff7c43746d9ac0a259f77ff494e))
- use no-proto objects for blockMaps
([13d113c5](https://github.com/angular/angular.js/commit/13d113c522f124b91a1fd8606c22bbd399abf121))
- move work to compile fn
([bdd853cb](https://github.com/angular/angular.js/commit/bdd853cb83839eef9901af164293611eaa23ee2c))
- move updateScope fn to factory and reuse it for all repeaters
([e58d65a5](https://github.com/angular/angular.js/commit/e58d65a520cfbc630cbfbc248479416777ca16b2))
- clone boundary comment nodes
([fbd48845](https://github.com/angular/angular.js/commit/fbd48845e0e88e9935f82fe4c9f686ad78b5d924))
## Breaking Changes
- **$compile:**
- due to [09de7b5d](https://github.com/angular/angular.js/commit/09de7b5db466498becb295ecf5c1d0a698b1512c),
Now, `ng-attr-*` will never add the attribute to the DOM if any of the interpolated expressions
evaluate to `undefined`.
To work around this, initialize values which are intended to be the empty string with the
empty string:
For example, given the following markup:
```html
<div ng-attr-style="border-radius: {{value}}{{units}}"></div>
```
If `$scope.value` is `4`, and `$scope.units` is `undefined`, the resulting markup is unchanged:
```html
<div ng-attr-style="border-radius: {{value}}{{units}}"></div>
```
However, if $scope.units is `""`, then the resulting markup is updated:
```html
<div ng-attr-style="border-radius: {{value}}{{units}}" style="border-radius: 4"></div>
```
Closes #8376
Closes #8399
- due to [0d608d04](https://github.com/angular/angular.js/commit/0d608d041f37a659d8d8ba7a9b688e132587035d),
element-transcluded directives now have an extra comment automatically appended to their cloned DOM
This comment is usually needed to keep track the end boundary in the event child directives modify the root node(s).
If not used for this purpose it can be safely ignored.
- due to [75c4cbf8](https://github.com/angular/angular.js/commit/75c4cbf81fcd6d49656d3cb044e59e5fd24e0479),
`directive.type` was renamed to `directive.templateNamespace`
This change is breaking only within 1.3.0-beta releases: `directive.type` was renamed to `directive.templateNamespace`
The property name `type` was too general.
- **$parse:** due to [8863b9d0](https://github.com/angular/angular.js/commit/8863b9d04c722b278fa93c5d66ad1e578ad6eb1f),
`this` in filters is now undefined and no longer the scope
It's a bad practice for filters to have hidden dependencies, so pulling stuff from scope directly
is not a good idea. Scope being the filter context was never documented as public API, so we don't
expect that any significant code depends on this behavior.
If an existing filter has a dependency on the scope instance, the scope reference can
be passed into the filter as a filter argument (this is highly discouraged for new code):
Before: `{{ user.name | customFilter }}`
After: `{{ user.name | customFilter:this }}`
- **Scope:** due to [0554c1aa](https://github.com/angular/angular.js/commit/0554c1aae49a81691154a77e70b602b0f24dca81),
`deregisterNotifier` callback for `$watch` is no longer available
This API was available only in the last few 1.3 beta versions and is not
very useful for applications, so we don't expect that anyone will be affected
by this change.
- **input:** due to [a7fb357f](https://github.com/angular/angular.js/commit/a7fb357fa122e0a056ce1de838a2dfaf1ebc2953),
by default, do not trim `input[type=password]` values.
Previously, `input[type=password]` would trim values by default, and would require an explicit `ng-trim="false"`
to disable the trimming behaviour. After this change, `ng-trim` no longer affects `input[type=password]`, and will
never trim the password value.
Closes #8250
Closes #8230
<a name="1.3.0-beta.18"></a>
# 1.3.0-beta.18 spontaneous-combustion (2014-08-12)
## Bug Fixes
- **$compile:** make '='-bindings NaN-aware
([5038bf79](https://github.com/angular/angular.js/commit/5038bf79c6c8251d7449d887b44a4321e619c534),
[#8553](https://github.com/angular/angular.js/issues/8553), [#8554](https://github.com/angular/angular.js/issues/8554))
- **$location:** add semicolon to whitelist of delimiters to unencode
([36258033](https://github.com/angular/angular.js/commit/3625803349de04f175f87a22cbb608738003811a),
[#5019](https://github.com/angular/angular.js/issues/5019))
- **$parse:**
- one-time binding for literal expressions works as expected
([c024f282](https://github.com/angular/angular.js/commit/c024f28217cf8eedd695dd4b933ecf2ba4243c15),
[#8209](https://github.com/angular/angular.js/issues/8209))
- correctly assign expressions who's path is undefined and that use brackets notation
([c03ad249](https://github.com/angular/angular.js/commit/c03ad249033e701f3ad7aa358102e1cb87f5025c),
[#8039](https://github.com/angular/angular.js/issues/8039))
- **Scope:** add deregisterNotifier to oneTimeLiteralWatch signature
([a001a417](https://github.com/angular/angular.js/commit/a001a417d5c12bad0fa09c88e045622b95239e2f))
- **jqLite:**
- allow `triggerHandler()` to accept custom event
([01d81cda](https://github.com/angular/angular.js/commit/01d81cdab3dbbcb8b4204769eb5272096eb0837f),
[#8469](https://github.com/angular/angular.js/issues/8469))
- fix regression where mutating the dom tree on a event breaks jqLite.remove
([a00c9bca](https://github.com/angular/angular.js/commit/a00c9bca401abe5b5b0a217be82333056422c811),
[#8359](https://github.com/angular/angular.js/issues/8359))
- **ngSanitize:** ensure `html` is a string in htmlParser()
([34781f18](https://github.com/angular/angular.js/commit/34781f18cb75ded9ae29f4b78f5bacd079f76709),
[#8417](https://github.com/angular/angular.js/issues/8417), [#8416](https://github.com/angular/angular.js/issues/8416))
- **select:**
- ensure that at least one option has the `selected` attribute set
([25a476ea](https://github.com/angular/angular.js/commit/25a476ea096b200fb4f422aaa9cd7215e2596ad3),
[#8366](https://github.com/angular/angular.js/issues/8366), [#8429](https://github.com/angular/angular.js/issues/8429))
- do not update selected property of an option element on digest with no change event
([cdc7db3f](https://github.com/angular/angular.js/commit/cdc7db3f35368a9175ed96c63f4bf56593fe1876),
[#8221](https://github.com/angular/angular.js/issues/8221), [#7715](https://github.com/angular/angular.js/issues/7715))
## Features
- **$parse:** allow for assignments in ternary operator branches
([2d678f1d](https://github.com/angular/angular.js/commit/2d678f1d0a3714fdd49e582b92787312af129947),
[#8512](https://github.com/angular/angular.js/issues/8512), [#8484](https://github.com/angular/angular.js/issues/8484))
- **form:** Add new $submitted state to forms
([108a69be](https://github.com/angular/angular.js/commit/108a69be17df5884d026c57b2be3235c576250fe),
[#8056](https://github.com/angular/angular.js/issues/8056))
- **http:** allow caching for JSONP requests
([3607c982](https://github.com/angular/angular.js/commit/3607c9822f57b4d01b3f09a6ae4efc7168bec6c5),
[#1947](https://github.com/angular/angular.js/issues/1947), [#8356](https://github.com/angular/angular.js/issues/8356))
- **jQuery:** upgrade to jQuery to 2.1.1
([9e7cb3c3](https://github.com/angular/angular.js/commit/9e7cb3c37543008e6236bb5a2c4536df2e1e43a9))
- **ngMock:** allow override of when/expect definitions
([477626d8](https://github.com/angular/angular.js/commit/477626d846b4de65d1d5c7071e6a94361395ff42),
[#5766](https://github.com/angular/angular.js/issues/5766), [#8352](https://github.com/angular/angular.js/issues/8352))
## Performance Improvements
- **$q:** move Deferred and Promise methods to prototypes
([23bc92b1](https://github.com/angular/angular.js/commit/23bc92b17df882a907fb326320f0622717fefe7b),
[#8300](https://github.com/angular/angular.js/issues/8300))
- **input:** prevent additional $digest when input is already touched
([dd2a803f](https://github.com/angular/angular.js/commit/dd2a803f4f03ab629a51623c026d3e3f9dc9e91f),
[#8450](https://github.com/angular/angular.js/issues/8450))
## Breaking Changes
- **jQuery:** due to [9e7cb3c3](https://github.com/angular/angular.js/commit/9e7cb3c37543008e6236bb5a2c4536df2e1e43a9),
Angular no longer supports jQuery versions below 2.1.1.
<a name="1.2.22"></a>
# 1.2.22 finicky-pleasure (2014-08-12)
## Bug Fixes
- **$compile:** make '='-bindings NaN-aware
([0b0acb03](https://github.com/angular/angular.js/commit/0b0acb03424a273965fa6e6175d584f53a90252c),
[#8553](https://github.com/angular/angular.js/issues/8553), [#8554](https://github.com/angular/angular.js/issues/8554))
- **$parse:** correctly assign expressions who's path is undefined and that use brackets notation
([60366c8d](https://github.com/angular/angular.js/commit/60366c8d0bb5ffdd1bd8a8971820eb4868f3efd5),
[#8039](https://github.com/angular/angular.js/issues/8039))
- **jqLite:** allow `triggerHandler()` to accept custom event
([d262378b](https://github.com/angular/angular.js/commit/d262378b7c047dcd925cf4b55b80c0697b292232),
[#8469](https://github.com/angular/angular.js/issues/8469), [#8505](https://github.com/angular/angular.js/issues/8505))
- **ngSanitize:** ensure `html` is a string in htmlParser()
([9ee07551](https://github.com/angular/angular.js/commit/9ee075518f1ccec0f34aa49bd007aa2ed9a3b12e),
[#8417](https://github.com/angular/angular.js/issues/8417), [#8416](https://github.com/angular/angular.js/issues/8416))
- **select:**
- ensure that at least one option has the `selected` attribute set
([79538afd](https://github.com/angular/angular.js/commit/79538afd7bd730d49be8eb988a3a54848d8ddaec),
[#8366](https://github.com/angular/angular.js/issues/8366), [#8429](https://github.com/angular/angular.js/issues/8429))
- do not update selected property of an option element on digest with no change event
([c2860944](https://github.com/angular/angular.js/commit/c2860944c61a0b910f703fe8a9717188ed387893),
[#8221](https://github.com/angular/angular.js/issues/8221), [#7715](https://github.com/angular/angular.js/issues/7715))
## Features
- **$parse:** allow for assignments in ternary operator branches
([93b0c2d8](https://github.com/angular/angular.js/commit/93b0c2d8925e354159cc421e5be1bca9582f7b70),
[#8512](https://github.com/angular/angular.js/issues/8512), [#8484](https://github.com/angular/angular.js/issues/8484))
- **http:** allow caching for JSONP requests
([eab5731a](https://github.com/angular/angular.js/commit/eab5731afc788c59f3f2988db372299268df8614),
[#1947](https://github.com/angular/angular.js/issues/1947), [#8356](https://github.com/angular/angular.js/issues/8356))
<a name="1.3.0-beta.17"></a>
# 1.3.0-beta.17 turing-autocompletion (2014-07-25)
## Bug Fixes
- **angular.copy:** clone regexp flags correctly
([86340a59](https://github.com/angular/angular.js/commit/86340a59bf9eb7bdfc4f99000cecf628cd10d9c8),
[#5781](https://github.com/angular/angular.js/issues/5781), [#8337](https://github.com/angular/angular.js/issues/8337))
- **docs:** change plnkr form to open in same window
([925b2080](https://github.com/angular/angular.js/commit/925b2080a0341d9348feeb4f492957a2e2c80082))
- **jqLite:** triggerHandler support unbind self
([8a27abae](https://github.com/angular/angular.js/commit/8a27abae896de3c4d94c407e8bb381e099d2d7f7),
[#5984](https://github.com/angular/angular.js/issues/5984))
- **ngHref:** remove attribute when empty value instead of ignoring
([469ea338](https://github.com/angular/angular.js/commit/469ea3384ad48ca4765af807c0f41201edb527f9),
[#2755](https://github.com/angular/angular.js/issues/2755))
## Features
- **$compile:** change directive's restrict setting to default to EA (element/attribute)
([11f5aeee](https://github.com/angular/angular.js/commit/11f5aeeee952a395edaf54e3277674f211a82fc7),
[#8321](https://github.com/angular/angular.js/issues/8321))
- **$q:** add streamlined ES6-style interface for using $q
([f3a763fd](https://github.com/angular/angular.js/commit/f3a763fd2edd8a37b80c79a5aaa1444460cd2df7),
[#8311](https://github.com/angular/angular.js/issues/8311), [#6427](https://github.com/angular/angular.js/issues/6427))
- **ngRepeat:** provide support for aliasing filtered repeater results as a scope member
([e0adb9c4](https://github.com/angular/angular.js/commit/e0adb9c452e172295209f785b62472688225fffb),
[#5919](https://github.com/angular/angular.js/issues/5919), [#8046](https://github.com/angular/angular.js/issues/8046), [#8282](https://github.com/angular/angular.js/issues/8282))
## Performance Improvements
- **$parse:** don't use reflective calls in generated functions
([c54228fb](https://github.com/angular/angular.js/commit/c54228fbe9d42d8a3a159bf84dd1d2e99b259ece))
## Breaking Changes
- **$compile:** due to [11f5aeee](https://github.com/angular/angular.js/commit/11f5aeeee952a395edaf54e3277674f211a82fc7),
directives now match elements by default unless specific restriction rules are set via `restrict` property.
This means that if a directive 'myFoo' previously didn't specify matching restrictrion, it will now match both the attribute
and element form.
Before:
`<div my-foo></div>` <---- my-foo attribute matched the directive
`<my-foo></my-foo>` <---- no match
After:
`<div my-foo></div>` <---- my-foo attribute matched the directive
`<my-foo></my-foo>` <---- my-foo element matched the directive
It is not expected that this will be a problem in practice because of widespread use of prefixes that make `<my-foo>` like
elements unlikely.
Closes #8321
<a name="1.2.21"></a>
# 1.2.21 wizard-props (2014-07-25)
## Bug Fixes
- **$http:** fix double-quoted date issue when encoding params
([2f960f15](https://github.com/angular/angular.js/commit/2f960f1530ed936c57df612a352a0d996368f6a1),
[#8150](https://github.com/angular/angular.js/issues/8150), [#6128](https://github.com/angular/angular.js/issues/6128), [#8154](https://github.com/angular/angular.js/issues/8154))
- **$location:** handle plus character in query strings
([60af504c](https://github.com/angular/angular.js/commit/60af504c18dbdde9dfe90e9a2badef6d9e798512),
[#3042](https://github.com/angular/angular.js/issues/3042))
- **$rootScope:** $watchCollection should handle NaN in objects
([bf13d268](https://github.com/angular/angular.js/commit/bf13d2683d5880b18db00087e80ee0fd5e1f429a),
[#7930](https://github.com/angular/angular.js/issues/7930))
- **angular.copy:** clone regexp flags correctly
([e25ed0d4](https://github.com/angular/angular.js/commit/e25ed0d48d9a1c577e78b1c96098841572c764ea),
[#5781](https://github.com/angular/angular.js/issues/5781), [#8337](https://github.com/angular/angular.js/issues/8337))
- **csp:** fix autodetection of CSP + better docs
([0e5d3190](https://github.com/angular/angular.js/commit/0e5d31908e122f013427164f7bbeea914a9a5961),
[#8162](https://github.com/angular/angular.js/issues/8162), [#8191](https://github.com/angular/angular.js/issues/8191))
- **docs:** change plnkr form to open in same window
([5d11e020](https://github.com/angular/angular.js/commit/5d11e02008731a78f302841863a83fe7ed3c37b9))
- **jqLite:** triggerHandler support unbind self
([209e6000](https://github.com/angular/angular.js/commit/209e60007042f7e8b34c54ec6bf7d6f703c0ba2a),
[#5984](https://github.com/angular/angular.js/issues/5984))
- **ngHref:** remove attribute when empty value instead of ignoring
([948c86c6](https://github.com/angular/angular.js/commit/948c86c6025fca8e07921869d21cfac1c6333b05),
[#2755](https://github.com/angular/angular.js/issues/2755))
- **ngRoute:** remove unnecessary call to decodeURIComponent
([1b779028](https://github.com/angular/angular.js/commit/1b779028fdd339febaa1fff5f3bd4cfcda46cc09),
[#6326](https://github.com/angular/angular.js/issues/6326), [#6327](https://github.com/angular/angular.js/issues/6327))
- **ngSanitize:**
- follow HTML parser rules for start tags / allow < in text content
([d175bb01](https://github.com/angular/angular.js/commit/d175bb01314efdcbad5c3cb31b02e298e26c6e19),
[#8212](https://github.com/angular/angular.js/issues/8212), [#8193](https://github.com/angular/angular.js/issues/8193))
- **orderBy:** correctly order by date values
([f1b28847](https://github.com/angular/angular.js/commit/f1b28847c8123483e03ac2410de86fd33a80b5f4),
[#6675](https://github.com/angular/angular.js/issues/6675), [#6746](https://github.com/angular/angular.js/issues/6746))
- **select:** force visual update in IE
([c0afbfac](https://github.com/angular/angular.js/commit/c0afbfaca57893403d8d4b0990879ad5b9ffc3e5),
[#7692](https://github.com/angular/angular.js/issues/7692), [#8158](https://github.com/angular/angular.js/issues/8158))
## Performance Improvements
- **$compile:** only create jqLite object when necessary
([71eb1901](https://github.com/angular/angular.js/commit/71eb1901f6b9a3a6d4b772aa95ce0dc78ff847bc))
- **$parse:** don't use reflective calls in generated functions
([cbdf0c2a](https://github.com/angular/angular.js/commit/cbdf0c2afb9836ae4cca6d70cf555ff28f55a1d1))
- **forEach:** use native for loop instead of forEach for Arrays
([492b0cdf](https://github.com/angular/angular.js/commit/492b0cdf28d02f1d508455245b7d8e1d641d9f40))
- **jqLite:** expose the low-level jqLite.data/removeData calls
([3c46c943](https://github.com/angular/angular.js/commit/3c46c94342aa35131f3ba0f8f4a6b39338b87d56))
- **ngBindHtml:** move addClass to the compile phase
([8eede099](https://github.com/angular/angular.js/commit/8eede099cd8aa6d524d1de385d08432072fd294e),
[#8261](https://github.com/angular/angular.js/issues/8261))
<a name="1.3.0-beta.16"></a>
# 1.3.0-beta.16 pizza-transubstantiation (2014-07-18)
@@ -500,6 +1045,36 @@ Closes #3969
Closes #4277
Closes #7960
- **$timeout/$interval:**
- due to [19b6b343](https://github.com/angular/angular.js/commit/19b6b3433ae9f8523cbc72ae97dbcf0c06960148)
Previously, even if invokeApply was set to false, a $rootScope digest would occur during promise
resolution. This is no longer the case, as promises returned from $timeout and $interval will no
longer trigger $evalAsync (which in turn causes a $digest) if `invokeApply` is false.
Workarounds include manually triggering $scope.$apply(), or returning $q.defer().promise from a
promise callback, and resolving or rejecting it when appropriate.
var interval = $interval(function() {
if (someRequirementFulfilled) {
$interval.cancel(interval);
$scope.$apply();
}
}, 100, 0, false);
or:
var interval = $interval(function (idx) {
// make the magic happen
}, 1000, 10, false);
interval.then(function(idx) {
var deferred = $q.defer();
// do the asynchronous magic --- $evalAsync will cause a digest and cause
// bindings to update.
return deferred.promise;
});
<a name="1.2.19"></a>
# 1.2.19 precognitive-flashbacks (2014-06-30)
+4
View File
@@ -6,6 +6,10 @@
line-height: 1.5;
}
.lang-text * {
color: #333333!important;
}
.pln {
color: #333333;
}
@@ -37,7 +37,7 @@ describe('docs.angularjs.org', function () {
var nameInput = element(by.model('user.name'));
nameInput.sendKeys('!!!');
var code = element(by.css('tt'));
var code = element.all(by.css('tt')).first();
expect(code.getText()).toContain('guest!!!');
});
@@ -68,4 +68,21 @@ describe('docs.angularjs.org', function () {
expect(element(by.css('.minerr-errmsg')).getText()).toEqual("Argument 'Missing' is not a function, got undefined");
});
});
describe("templates", function() {
it("should show parameter defaults", function() {
browser.get('index-debug.html#!/api/ng/service/$timeout');
expect(element.all(by.css('.input-arguments p em')).first().getText()).toContain('(default: 0)');
});
});
describe("API pages", function() {
it("should display links to code on GitHub", function() {
browser.get('index-debug.html#!/api/ng/service/$http');
expect(element(by.css('.improve-docs')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/edit\/.+\/src\/ng\/http\.js/);
browser.get('index-debug.html#!/api/ng/service/$http');
expect(element(by.css('.view-source')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/tree\/.+\/src\/ng\/http\.js#L\d+/);
});
});
});
@@ -33,7 +33,8 @@ module.exports = {
innerTest: file.fileContents,
pathPrefix: '.', // Hold for if we test with full jQuery
exampleId: example.id,
description: example.doc.id
description: example.doc.id,
'ng-app-included': example['ng-app-included']
};
if (env === 'jquery') {
@@ -1,7 +1,10 @@
describe("{$ doc.description $}", function() {
var rootEl;
beforeEach(function() {
rootEl = browser.rootEl;{% if doc['ng-app-included'] %}
browser.rootEl = '[ng-app]';{% endif %}
browser.get("{$ doc.pathPrefix $}/{$ doc.examplePath $}");
});
{% if doc['ng-app-included'] %}afterEach(function() { browser.rootEl = rootEl; });{% endif %}
{$ doc.innerTest $}
});
+30
View File
@@ -25,3 +25,33 @@ angular.module('myApp', [])
// Do something with myService
}]);
```
An unknown provider error can also be caused by accidentally redefining a
module using the `angular.module` API, as shown in the following example.
```
angular.module('myModule', [])
.service('myCoolService', function () { /* ... */ });
angular.module('myModule', [])
// myModule has already been created! This is not what you want!
.directive('myDirective', ['myCoolService', function (myCoolService) {
// This directive definition throws unknown provider, because myCoolService
// has been destroyed.
}]);
```
To fix this problem, make sure you only define each module with the
`angular.module(name, [requires])` syntax once across your entire project.
Retrieve it for subsequent use with `angular.module(name)`. The fixed example
is shown below.
```
angular.module('myModule', [])
.service('myCoolService', function () { /* ... */ });
angular.module('myModule')
.directive('myDirective', ['myCoolService', function (myCoolService) {
// This directive definition does not throw unknown provider.
}]);
```
+5 -5
View File
@@ -135,7 +135,7 @@ These can be used interchangeably as you see fit and are equivalent.
### Implicit Dependencies
The simplest way to get hold of the dependencies, is to assume that the function parameter names
The simplest way to get hold of the dependencies is to assume that the function parameter names
are the names of the dependencies.
```js
@@ -144,7 +144,7 @@ function MyController($scope, greeter) {
}
```
Given a function the injector can infer the names of the service to inject by examining the
Given a function the injector can infer the names of the services to inject by examining the
function declaration and extracting the parameter names. In the above example `$scope`, and
`greeter` are two services which need to be injected into the function.
@@ -154,7 +154,7 @@ rename the method parameter names. This makes this way of annotating only useful
### `$inject` Property Annotation
To allow the minifiers to rename the function parameters and still be able to inject right services,
To allow the minifiers to rename the function parameters and still be able to inject the right services,
the function needs to be annotated with the `$inject` property. The `$inject` property is an array
of service names to inject.
@@ -166,7 +166,7 @@ MyController['$inject'] = ['$scope', 'greeter'];
```
In this scenario the ordering of the values in the `$inject` array must match the ordering of the
arguments to inject. Using above code snippet as an example, `$scope` will be injected into
arguments to inject. Using the above code snippet as an example, `$scope` will be injected into
`renamed$scope` and `greeter` into `renamedGreeter`. Care must be taken that the `$inject`
annotation is kept in sync with the actual arguments in the function declaration.
@@ -206,7 +206,7 @@ someModule.factory('greeter', ['$window', function(renamed$window) {
}]);
```
Here, instead of simply providing the factory function, we pass an array, whose elements consist of
Here, instead of simply providing the factory function, we pass an array whose elements consist of
a list of strings (the names of the dependencies) followed by the function itself.
Keep in mind that all of the annotation styles are equivalent and can be used anywhere in Angular
+24
View File
@@ -909,6 +909,30 @@ Looking back at `myPane`'s definition, notice the last argument in its `link` fu
When a directive requires a controller, it receives that controller as the fourth argument of its
`link` function. Taking advantage of this, `myPane` can call the `addPane` function of `myTabs`.
If multiple controllers are required, the `require` option of the directive can take an array argument.
The corresponding parameter being sent to the `link` function will also be an array.
```js
angular.module('docsTabsExample', [])
.directive('myPane', function() {
return {
require: ['^myTabs', '^ngModel'],
restrict: 'E',
transclude: true,
scope: {
title: '@'
},
link: function(scope, element, attrs, controllers) {
var tabsCtrl = controllers[0],
modelCtrl = controllers[1];
tabsCtrl.addPane(scope);
},
templateUrl: 'my-pane.html'
};
});
```
Savvy readers may be wondering what the difference is between `link` and `controller`.
The basic difference is that `controller` can expose an API, and `link` functions can interact with
controllers using `require`.
+1 -1
View File
@@ -47,7 +47,7 @@ In Angular applications, you move the job of filling page templates with data fr
### Testing
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/dev_guide.unit-testing Unit testing}, {@link guide/dev_guide.services.testing_services Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/unit-testing Unit testing}, {@link guide/services#unit-testing Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
* **Scenario testing:** [Protractor](https://github.com/angular/protractor)
## Specific Topics
+4 -2
View File
@@ -382,8 +382,6 @@ See [80739409](https://github.com/angular/angular.js/commit/807394095b991357225a
## ngBindHtmlUnsafe has been removed and replaced by ngBindHtml
`ngBindHtml` which has been moved from `ngSanitize` module to the core `ng` module.
`ngBindHtml` provides `ngBindHtmlUnsafe` like
behavior (evaluate an expression and innerHTML the result into the DOM) when bound to the result
of `$sce.trustAsHtml(string)`. When bound to a plain string, the string is sanitized via
@@ -391,6 +389,10 @@ of `$sce.trustAsHtml(string)`. When bound to a plain string, the string is sanit
module is not loaded) and the bound expression evaluates to a value that is not trusted an
exception is thrown.
When using this directive you can either include `ngSanitize` in your module's dependencis (See the
example at the {@link ngBindHtml} reference) or use the {@link $sce} service to set the value as
trusted.
See [dae69473](https://github.com/angular/angular.js/commit/dae694739b9581bea5dbc53522ec00d87b26ae55).
+6 -4
View File
@@ -26,10 +26,12 @@ should be bootstrapped. There are several advantages to this approach:
I'm in a hurry. How do I get a Hello World module working?
<example module='myApp'>
<example ng-app-included="true">
<file name="index.html">
<div>
{{ 'World' | greet }}
<div ng-app="myApp">
<div>
{{ 'World' | greet }}
</div>
</div>
</file>
@@ -56,7 +58,7 @@ I'm in a hurry. How do I get a Hello World module working?
Important things to notice:
* The {@link angular.Module Module} API
* The reference to `myApp` module in `<html ng-app="myApp">`.
* The reference to `myApp` module in `<div ng-app="myApp">`.
This is what bootstraps the app using your module.
* The empty array in `angular.module('myApp', [])`.
This array is the list of modules `myApp` depends on.
+5 -5
View File
@@ -62,13 +62,13 @@ minified AngularJS files:
```shell
# Clone your Github repository:
git clone git@github.com:<github username>/angular.js.git
git clone "git@github.com:<github username>/angular.js.git"
# Go to the AngularJS directory:
cd angular.js
# Add the main AngularJS repository as an upstream remote to your repository:
git remote add upstream https://github.com/angular/angular.js.git
git remote add upstream "https://github.com/angular/angular.js.git"
# Install node.js dependencies:
npm install
@@ -126,13 +126,13 @@ made available a local web server based on Node.js.
```
2. To access the local server, enter the following URL into your web browser:
```
```text
http://localhost:8000/
```
By default, it serves the contents of the AngularJS project directory.
3. To access the locally served docs, visit this URL:
```
```text
http://localhost:8000/build/docs/
```
@@ -165,7 +165,7 @@ change. To execute tests in this mode run:
2. To capture more browsers, open this URL in the desired browser (URL might be different if you have multiple instance
of Karma running, read Karma's console output for the correct URL):
```shell
```text
http://localhost:9876/
```
+2 -2
View File
@@ -44,8 +44,8 @@ really digging into it. If you're looking for a shorter introduction to AngularJ
# Get Started
The rest of this page explains how you can set up your machine to work with the code on your local
machine. If you just want to read the tutorial then you can just go straight to the first step:
The rest of this page explains how you can set up your local machine for development.
If you just want to read the tutorial then you can just go straight to the first step:
[Step 0 - Bootstrapping](tutorial/step_00).
# Working with the code
+6 -1
View File
@@ -59,7 +59,7 @@ tag as the template.
by the value of the expressions.
We have added a new directive, called `ng-controller`, which attaches a `PhoneListCtrl`
__controller__ to the DOM at this point:
__controller__ to the &lt;body&gt; tag. At this point:
* The expressions in curly braces (`{{phone.name}}` and `{{phone.snippet}}` denote
bindings, which are referring to our application model, which is set up in our `PhoneListCtrl`
@@ -210,6 +210,11 @@ To run the tests, and then watch the files for changes: `npm test`.
* To rerun the tests, just change any of the source or test .js files. Karma will notice the change
and will rerun the tests for you. Now isn't that sweet?
<div class="alert alert-info">
Make sure you don't minimize the browser that Karma opened. On some OS, memory assigned to a minimized
browser is limited, which results in your karma tests running extremely slow.
</div>
# Experiments
* Add another binding to `index.html`. For example:
+30 -7
View File
@@ -145,16 +145,39 @@ Display the current value of the `query` model by adding a `{{query}}` binding i
### Display Query in Title
Let's see how we can get the current value of the `query` model to appear in the HTML page title.
* Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`:
* Add an end-to-end test into the `describe` block, `test/e2e/scenarios.js` should look like this:
```js
it('should display the current filter value in the title bar', function() {
describe('PhoneCat App', function() {
expect(browser.getTitle()).toMatch(/Google Phone Gallery:\s*$/);
element(by.model('query')).sendKeys('nexus');
expect(browser.getTitle()).toMatch(/Google Phone Gallery: nexus$/);
describe('Phone list view', function() {
beforeEach(function() {
browser.get('app/index.html');
});
var phoneList = element.all(by.repeater('phone in phones'));
var query = element(by.model('query'));
it('should filter the phone list as user types into the search box', function() {
expect(phoneList.count()).toBe(3);
query.sendKeys('nexus');
expect(phoneList.count()).toBe(1);
query.clear();
query.sendKeys('motorola');
expect(phoneList.count()).toBe(2);
});
it('should display the current filter value in the title bar', function() {
query.clear();
expect(browser.getTitle()).toMatch(/Google Phone Gallery:\s*$/);
query.sendKeys('nexus');
expect(browser.getTitle()).toMatch(/Google Phone Gallery: nexus$/);
});
});
});
```
+1 -1
View File
@@ -202,7 +202,7 @@ moved the controllers into their own module `phonecatControllers` (as shown belo
We added `angular-route.js` to `index.html` and created a new `phonecatControllers` module in
`controllers.js`. That's not all we need to do to be able to use their code, however. We also have
to add the modules dependencies of our app. By listing these two modules as dependencies of
to add the modules as dependencies of our app. By listing these two modules as dependencies of
`phonecatApp`, we can use the directives and services they provide.
+44
View File
@@ -102,6 +102,50 @@ __`test/e2e/scenarios.js`:__
You can now rerun `npm run protractor` to see the tests run.
You also have to refactor one of your unit tests because of the addition of the `mainImageUrl`
model property to the `PhoneDetailCtrl` controller. Below, we create the function `xyzPhoneData`
which returns the appropriate json with the `images` attribute in order to get the test to pass.
__`test/unit/controllersSpec.js`:__
```js
...
beforeEach(module('phonecatApp'));
...
describe('PhoneDetailCtrl', function(){
var scope, $httpBackend, ctrl,
xyzPhoneData = function() {
return {
name: 'phone xyz',
images: ['image/url1.png', 'image/url2.png']
}
};
beforeEach(inject(function(_$httpBackend_, $rootScope, $routeParams, $controller) {
$httpBackend = _$httpBackend_;
$httpBackend.expectGET('phones/xyz.json').respond(xyzPhoneData());
$routeParams.phoneId = 'xyz';
scope = $rootScope.$new();
ctrl = $controller('PhoneDetailCtrl', {$scope: scope});
}));
it('should fetch phone detail', function() {
expect(scope.phone).toBeUndefined();
$httpBackend.flush();
expect(scope.phone).toEqual(xyzPhoneData());
});
});
```
Your unit tests should now be passing.
# Experiments
* Let's add a new controller method to `PhoneDetailCtrl`:
+5 -4
View File
@@ -12,9 +12,9 @@ set -e
# before_script:
# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash
CONNECT_URL="http://saucelabs.com/downloads/Sauce-Connect-latest.zip"
CONNECT_URL="https://d2nkw87yt5k0to.cloudfront.net/downloads/sc-4.3-linux.tar.gz"
CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
CONNECT_DOWNLOAD="Sauce_Connect.zip"
CONNECT_DOWNLOAD="sc-4.3-linux.tar.gz"
CONNECT_LOG="$LOGS_DIR/sauce-connect"
CONNECT_STDOUT="$LOGS_DIR/sauce-connect.stdout"
@@ -24,7 +24,8 @@ CONNECT_STDERR="$LOGS_DIR/sauce-connect.stderr"
mkdir -p $CONNECT_DIR
cd $CONNECT_DIR
curl $CONNECT_URL -o $CONNECT_DOWNLOAD 2> /dev/null 1> /dev/null
unzip $CONNECT_DOWNLOAD > /dev/null
mkdir sauce-connect
tar --extract --file=$CONNECT_DOWNLOAD --strip-components=1 --directory=sauce-connect > /dev/null
rm $CONNECT_DOWNLOAD
SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
@@ -45,5 +46,5 @@ echo "Starting Sauce Connect in the background, logging into:"
echo " $CONNECT_LOG"
echo " $CONNECT_STDOUT"
echo " $CONNECT_STDERR"
java -jar Sauce-Connect.jar $ARGS $SAUCE_USERNAME $SAUCE_ACCESS_KEY \
sauce-connect/bin/sc -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY $ARGS -v \
--logfile $CONNECT_LOG 2> $CONNECT_STDERR 1> $CONNECT_STDOUT &
+1 -1
View File
@@ -502,7 +502,7 @@
}
},
"dgeni-packages": {
"version": "0.9.6",
"version": "0.9.7",
"dependencies": {
"lodash": {
"version": "2.4.1"
+1 -1
View File
@@ -48,7 +48,7 @@
"canonical-path": "0.0.2",
"winston": "~0.7.2",
"dgeni": "^0.3.0",
"dgeni-packages": "^0.9.6",
"dgeni-packages": "^0.9.7",
"gulp-jshint": "~1.4.2",
"jshint-stylish": "~0.1.5",
"node-html-encoder": "0.0.2",
+1 -1
View File
@@ -4,7 +4,7 @@ var config = require('./protractor-shared-conf').config;
config.specs = [
'build/docs/ptore2e/**/*.js',
'test/e2e/docsAppE2E.js'
'docs/app/e2e/docsAppE2E.js'
];
config.capabilities = {
+1 -1
View File
@@ -5,7 +5,7 @@ exports.config = {
specs: [
'build/docs/ptore2e/**/*.js',
'test/e2e/docsAppE2E.js'
'docs/app/e2e/docsAppE2E.js'
],
capabilities: {
+3 -1
View File
@@ -7,9 +7,11 @@ config.sauceKey = process.env.SAUCE_ACCESS_KEY;
config.multiCapabilities = [{
'browserName': 'chrome',
'platform': 'OS X 10.9',
'name': 'Angular E2E',
'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
'build': process.env.TRAVIS_BUILD_NUMBER
'build': process.env.TRAVIS_BUILD_NUMBER,
'version': '34'
}, {
'browserName': 'firefox',
'name': 'Angular E2E',
+1 -1
View File
@@ -14,7 +14,7 @@ elif [ $JOB = "e2e" ]; then
if [ $TEST_TARGET = "jquery" ]; then
TARGET_SPECS="build/docs/ptore2e/**/*jquery_test.js"
elif [ $TEST_TARGET = "doce2e" ]; then
TARGET_SPECS="test/e2e/docsAppE2E.js"
TARGET_SPECS="docs/app/e2e/docsAppE2E.js"
fi
grunt test:travis-protractor --specs "$TARGET_SPECS"
else
@@ -6,4 +6,4 @@ set -e
cd $(dirname $0);
cd ../..
curl "http://23.251.148.50:8000/tar/$TRAVIS_REPO_SLUG/$TRAVIS_COMMIT" | tar xz
curl "http://23.251.148.50:8000/tar/$TRAVIS_REPO_SLUG/$TRAVIS_COMMIT" | tar xz || true
+14 -5
View File
@@ -820,9 +820,13 @@ function copy(source, destination, stackSource, stackDest) {
}
} else {
var h = destination.$$hashKey;
forEach(destination, function(value, key) {
delete destination[key];
});
if (isArray(destination)) {
destination.length = 0;
} else {
forEach(destination, function(value, key) {
delete destination[key];
});
}
for ( var key in source) {
result = copy(source[key], null, stackSource, stackDest);
if (isObject(source[key])) {
@@ -907,7 +911,8 @@ function equals(o1, o2) {
return true;
}
} else if (isDate(o1)) {
return isDate(o2) && o1.getTime() == o2.getTime();
if (!isDate(o2)) return false;
return (isNaN(o1.getTime()) && isNaN(o2.getTime())) || (o1.getTime() === o2.getTime());
} else if (isRegExp(o1) && isRegExp(o2)) {
return o1.toString() == o2.toString();
} else {
@@ -1345,7 +1350,11 @@ function bootstrap(element, modules) {
if (element.injector()) {
var tag = (element[0] === document) ? 'document' : startingTag(element);
throw ngMinErr('btstrpd', "App Already Bootstrapped with this Element '{0}'", tag);
//Encode angle brackets to prevent input from being sanitized to empty string #8683
throw ngMinErr(
'btstrpd',
"App Already Bootstrapped with this Element '{0}'",
tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
}
modules = modules || [];
+3 -3
View File
@@ -109,11 +109,11 @@ function publishExternalAPI(angular){
'element': jqLite,
'forEach': forEach,
'injector': createInjector,
'noop':noop,
'bind':bind,
'noop': noop,
'bind': bind,
'toJson': toJson,
'fromJson': fromJson,
'identity':identity,
'identity': identity,
'isUndefined': isUndefined,
'isDefined': isDefined,
'isString': isString,
+28 -12
View File
@@ -952,21 +952,37 @@ forEach({
clone: jqLiteClone,
triggerHandler: function(element, eventName, eventData) {
// Copy event handlers in case event handlers array is modified during execution.
var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName],
eventFnsCopy = shallowCopy(eventFns || []);
triggerHandler: function(element, event, extraParameters) {
eventData = eventData || [];
var dummyEvent, eventFnsCopy, handlerArgs;
var eventName = event.type || event;
var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName];
var event = [{
preventDefault: noop,
stopPropagation: noop
}];
if (eventFns) {
forEach(eventFnsCopy, function(fn) {
fn.apply(element, event.concat(eventData));
});
// Create a dummy event to pass to the handlers
dummyEvent = {
preventDefault: function() { this.defaultPrevented = true; },
isDefaultPrevented: function() { return this.defaultPrevented === true; },
stopPropagation: noop,
type: eventName,
target: element
};
// If a custom event was provided then extend our dummy event with it
if (event.type) {
dummyEvent = extend(dummyEvent, event);
}
// Copy event handlers in case event handlers array is modified during execution.
eventFnsCopy = shallowCopy(eventFns);
handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
forEach(eventFnsCopy, function(fn) {
fn.apply(element, handlerArgs);
});
}
}
}, function(fn, name){
/**
+3 -2
View File
@@ -114,7 +114,7 @@ function setupModuleLoader(window) {
* @ngdoc property
* @name angular.Module#requires
* @module ng
* @returns {Array.<string>} List of module names which must be loaded before this module.
*
* @description
* Holds the list of modules which the injector will load before the current module is
* loaded.
@@ -125,8 +125,9 @@ function setupModuleLoader(window) {
* @ngdoc property
* @name angular.Module#name
* @module ng
* @returns {string} Name of the module.
*
* @description
* Name of the module.
*/
name: name,
+4 -2
View File
@@ -67,8 +67,10 @@
$scope.keys = [];
$scope.cache = $cacheFactory('cacheId');
$scope.put = function(key, value) {
$scope.cache.put(key, value);
$scope.keys.push(key);
if ($scope.cache.get(key) === undefined) {
$scope.keys.push(key);
}
$scope.cache.put(key, value === undefined ? null : value);
};
}]);
</file>
+9 -7
View File
@@ -191,9 +191,9 @@
*
* * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
* * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
* * `^` - Locate the required controller by searching the element's parents. Throw an error if not found.
* * `?^` - Attempt to locate the required controller by searching the element's parents or pass `null` to the
* `link` fn if not found.
* * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
* * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
* `null` to the `link` fn if not found.
*
*
* #### `controllerAs`
@@ -214,7 +214,7 @@
*
* #### `template`
* HTML markup that may:
* * Replace the contents of the directive's element (defualt).
* * Replace the contents of the directive's element (default).
* * Replace the directive's element itself (if `replace` is true - DEPRECATED).
* * Wrap the contents of the directive's element (if `transclude` is true).
*
@@ -1474,7 +1474,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (parentGet.literal) {
compare = equals;
} else {
compare = function(a,b) { return a === b; };
compare = function(a,b) { return a === b || (a !== a && b !== b); };
}
parentSet = parentGet.assign || function() {
// reset the change, or we will throw this exception on every $digest
@@ -2011,8 +2011,10 @@ function directiveNormalize(name) {
/**
* @ngdoc property
* @name $compile.directive.Attributes#$attr
* @returns {object} A map of DOM element attribute names to the normalized name. This is
* needed to do reverse lookup from normalized name back to actual name.
*
* @description
* A map of DOM element attribute names to the normalized name. This is
* needed to do reverse lookup from normalized name back to actual name.
*/
+7 -3
View File
@@ -160,7 +160,7 @@
*
* @description
*
* The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
* We shouldn't do this, because it will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
* ```html
* <div ng-init="scope = { isDisabled: false }">
* <button disabled="{{scope.isDisabled}}">Disabled</button>
@@ -380,8 +380,12 @@ forEach(['src', 'srcset', 'href'], function(attrName) {
}
attr.$observe(normalized, function(value) {
if (!value)
return;
if (!value) {
if (attrName === 'href') {
attr.$set(name, null);
}
return;
}
attr.$set(name, value);
+13 -3
View File
@@ -17,7 +17,9 @@ var inputType = {
* @name input[text]
*
* @description
* Standard HTML text input with angular data binding.
* Standard HTML text input with angular data binding, inherited by most of the `input` elements.
*
* *NOTE* Not every feature offered is available for all input types.
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
@@ -35,6 +37,8 @@ var inputType = {
* @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.
* This parameter is ignored for input[type=password] controls, which will never trim the
* input.
*
* @example
<example name="text-input-directive" module="textInputExample">
@@ -474,6 +478,7 @@ function addNativeHtml5Validators(ctrl, validatorName, badFlags, ignoreFlags, va
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
var validity = element.prop(VALIDITY_STATE_PROPERTY);
var placeholder = element[0].placeholder, noevent = {};
var type = lowercase(element[0].type);
ctrl.$$validityState = validity;
// In composition mode, users are still inputing intermediate text buffer,
@@ -507,8 +512,8 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
// By default we will trim the value
// If the attribute ng-trim exists we will avoid trimming
// e.g. <input ng-model="foo" ng-trim="false">
if (toBoolean(attr.ngTrim || 'T')) {
// If input type is 'password', the value is never trimmed
if (type !== 'password' && (toBoolean(attr.ngTrim || 'T'))) {
value = trim(value);
}
@@ -783,6 +788,8 @@ function checkboxInputType(scope, element, attr, ctrl) {
* HTML input element control with angular data-binding. Input control follows HTML5 input types
* and polyfills the HTML5 validation behavior for older browsers.
*
* *NOTE* Not every feature offered is available for all input types.
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} required Sets `required` validation error key if the value is not entered.
@@ -796,6 +803,9 @@ function checkboxInputType(scope, element, attr, ctrl) {
* patterns defined as scope expressions.
* @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.
* This parameter is ignored for input[type=password] controls, which will never trim the
* input.
*
* @example
<example name="input-directive" module="inputExample">
-1
View File
@@ -150,7 +150,6 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
* @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
*
* @example
Try it here: enter text in text box and watch the greeting change.
<example module="bindHtmlExample" deps="angular-sanitize.js">
<file name="index.html">
+7
View File
@@ -307,6 +307,13 @@ forEach(
* server and reloading the current page), but only if the form does not contain `action`,
* `data-action`, or `x-action` attributes.
*
* <div class="alert alert-warning">
* **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
* `ngSubmit` handlers together. See the
* {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
* for a detailed discussion of when `ngSubmit` may be triggered.
* </div>
*
* @element form
* @priority 0
* @param {expression} ngSubmit {@link guide/expression Expression} to eval.
+27 -21
View File
@@ -395,21 +395,37 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
value = valueFn(scope, locals);
}
}
// Update the null option's selected property here so $render cleans it up correctly
if (optionGroupsCache[0].length > 1) {
if (optionGroupsCache[0][1].id !== key) {
optionGroupsCache[0][1].selected = false;
}
}
}
ctrl.$setViewValue(value);
render();
});
});
ctrl.$render = render;
// TODO(vojta): can't we optimize this ?
scope.$watch(render);
scope.$watchCollection(valuesFn, render);
if ( multiple ) {
scope.$watchCollection(function() { return ctrl.$modelValue; }, render);
}
function getSelectedSet() {
var selectedSet = false;
if (multiple) {
var modelValue = ctrl.$modelValue;
if (trackFn && isArray(modelValue)) {
selectedSet = new HashMap([]);
var locals = {};
for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
locals[valueName] = modelValue[trackIndex];
selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
}
} else {
selectedSet = new HashMap(modelValue);
}
}
return selectedSet;
}
function render() {
// Temporary location for the option groups before we render them
@@ -427,22 +443,11 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
groupIndex, index,
locals = {},
selected,
selectedSet = false, // nothing is selected yet
selectedSet = getSelectedSet(),
lastElement,
element,
label;
if (multiple) {
if (trackFn && isArray(modelValue)) {
selectedSet = new HashMap([]);
for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
locals[valueName] = modelValue[trackIndex];
selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
}
} else {
selectedSet = new HashMap(modelValue);
}
}
// We now build up the list of options we need (we merge later)
for (index = 0; length = keys.length, index < length; index++) {
@@ -538,7 +543,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
lastElement.val(existingOption.id = option.id);
}
// lastElement.prop('selected') provided by jQuery has side-effects
if (existingOption.selected !== option.selected) {
if (lastElement[0].selected !== option.selected) {
lastElement.prop('selected', (existingOption.selected = option.selected));
if (msie) {
// See #7692
@@ -561,6 +566,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
(element = optionTemplate.clone())
.val(option.id)
.prop('selected', option.selected)
.attr('selected', option.selected)
.text(option.label);
}
+1 -1
View File
@@ -327,7 +327,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
* (e.g. `"h 'o''clock'"`).
*
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
* specified in the string input, the time is considered to be in the local timezone.
* @param {string=} format Formatting rules (see Description). If not specified,
+7 -3
View File
@@ -18,9 +18,13 @@
*
* - `function`: Getter function. The result of this function will be sorted using the
* `<`, `=`, `>` operator.
* - `string`: An Angular expression which evaluates to an object to order by, such as 'name'
* to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control
* ascending or descending sort order (for example, +name or -name).
* - `string`: An Angular expression. The result of this expression is used to compare elements
* (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
* 3 first characters of a property called `name`). The result of a constant expression
* is interpreted as a property name to be used in comparisons (for example `"special name"`
* to sort object by the value of their `special name` property). An expression can be
* optionally prefixed with `+` or `-` to control ascending or descending sort order
* (for example, `+name` or `-name`).
* - `Array`: An array of function or string predicates. The first predicate in the array
* is used for sorting, but when two items are equivalent, the next predicate is used.
*
+5 -3
View File
@@ -275,6 +275,7 @@ function $HttpProvider() {
* - {@link ng.$http#put $http.put}
* - {@link ng.$http#delete $http.delete}
* - {@link ng.$http#jsonp $http.jsonp}
* - {@link ng.$http#patch $http.patch}
*
*
* # Setting HTTP Headers
@@ -539,7 +540,7 @@ function $HttpProvider() {
* that only JavaScript running on your domain could have sent the request. The token must be
* unique for each user and must be verifiable by the server (to prevent the JavaScript from
* making up its own tokens). We recommend that the token is a digest of your site's
* authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
* authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
* for added security.
*
* The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
@@ -576,7 +577,7 @@ function $HttpProvider() {
* - **timeout** `{number|Promise}` timeout in milliseconds, or {@link ng.$q promise}
* that should abort the request when resolved.
* - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
* XHR object. See [requests with credentials]https://developer.mozilla.org/en/http_access_control#section_5
* XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
* for more information.
* - **responseType** - `{string}` - see
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
@@ -945,7 +946,8 @@ function $HttpProvider() {
promise.then(removePendingReq, removePendingReq);
if ((config.cache || defaults.cache) && config.cache !== false && config.method == 'GET') {
if ((config.cache || defaults.cache) && config.cache !== false &&
(config.method === 'GET' || config.method === 'JSONP')) {
cache = isObject(config.cache) ? config.cache
: isObject(defaults.cache) ? defaults.cache
: defaultCache;
+1 -1
View File
@@ -80,7 +80,7 @@ function $IntervalProvider() {
* };
*
* $scope.$on('$destroy', function() {
* // Make sure that the interval nis destroyed too
* // Make sure that the interval is destroyed too
* $scope.stopFight();
* });
* }])
+7 -1
View File
@@ -635,6 +635,8 @@ function $LocationProvider(){
$location = new LocationMode(appBase, '#' + hashPrefix);
$location.$$parse($location.$$rewrite(initialUrl));
var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
$rootElement.on('click', function(event) {
// TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
// currently we open nice url link and redirect then
@@ -657,6 +659,9 @@ function $LocationProvider(){
absHref = urlResolve(absHref.animVal).href;
}
// Ignore when url is started with javascript: or mailto:
if (IGNORE_URI_REGEXP.test(absHref)) return;
// Make relative links work in HTML5 mode for legacy browsers (or at least IE8 & 9)
// The href should be a regular url e.g. /link/somewhere or link/somewhere or ../somewhere or
// somewhere#anchor or http://example.com/somewhere
@@ -665,7 +670,7 @@ function $LocationProvider(){
// http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
var href = elm.attr('href') || elm.attr('xlink:href');
if (href.indexOf('://') < 0) { // Ignore absolute URLs
if (href && href.indexOf('://') < 0) { // Ignore absolute URLs
var prefix = '#' + hashPrefix;
if (href[0] == '/') {
// absolute path - replace old path
@@ -677,6 +682,7 @@ function $LocationProvider(){
// relative path - join with current path
var stack = $location.path().split("/"),
parts = href.split("/");
if (stack.length === 2 && !stack[1]) stack.length = 1;
for (var i=0; i<parts.length; i++) {
if (parts[i] == ".")
continue;
+9 -6
View File
@@ -606,9 +606,9 @@ Parser.prototype = {
var middle;
var token;
if ((token = this.expect('?'))) {
middle = this.ternary();
middle = this.assignment();
if ((token = this.expect(':'))) {
return this.ternaryFn(left, middle, this.ternary());
return this.ternaryFn(left, middle, this.assignment());
} else {
this.throwError('expected :', token);
}
@@ -696,7 +696,9 @@ Parser.prototype = {
return getter(self || object(scope, locals));
}, {
assign: function(scope, value, locals) {
return setter(object(scope, locals), field, value, parser.text, parser.options);
var o = object(scope, locals);
if (!o) object.assign(scope, o = {});
return setter(o, field, value, parser.text, parser.options);
}
});
},
@@ -726,10 +728,11 @@ Parser.prototype = {
return v;
}, {
assign: function(self, value, locals) {
var key = indexFn(self, locals);
var key = ensureSafeMemberName(indexFn(self, locals), parser.text);
// prevent overwriting of Function.constructor which would break ensureSafeObject check
var safe = ensureSafeObject(obj(self, locals), parser.text);
return safe[key] = value;
var o = ensureSafeObject(obj(self, locals), parser.text);
if (!o) obj.assign(self, o = {});
return o[key] = value;
}
});
},
+1 -1
View File
@@ -6,7 +6,7 @@
*/
function $$SanitizeUriProvider() {
var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file):|data:image\/)/;
/**
* @description
+3 -1
View File
@@ -1779,7 +1779,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
* use the `passThrough` request handler of `when` instead of `respond`.
*
* Additionally, we don't want to manually have to flush mocked out requests like we do during unit
* testing. For this reason the e2e $httpBackend automatically flushes mocked out requests
* testing. For this reason the e2e $httpBackend flushes mocked out requests
* automatically, closely simulating the behavior of the XMLHttpRequest object.
*
* To setup the application to run with this http backend, you have to create a module that depends
@@ -1996,6 +1996,7 @@ if(window.jasmine || window.mocha) {
* @description
*
* *NOTE*: This function is also published on window for easy access.<br>
* *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
*
* This function registers a module configuration code. It collects the configuration information
* which will be used when the injector is created by {@link angular.mock.inject inject}.
@@ -2038,6 +2039,7 @@ if(window.jasmine || window.mocha) {
* @description
*
* *NOTE*: This function is also published on window for easy access.<br>
* *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
*
* The inject function wraps a function into an injectable function. The inject() creates new
* instance of {@link auto.$injector $injector} per test, which is then used for
+4 -2
View File
@@ -94,8 +94,10 @@ function shallowClearAndCopy(src, dst) {
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
* URL `/path/greet?salutation=Hello`.
*
* If the parameter value is prefixed with `@` then the value of that parameter will be taken
* from the corresponding key on the data object (useful for non-GET operations).
* If the parameter value is prefixed with `@` then the value for that parameter will be extracted
* from the corresponding property on the `data` object (provided when calling an action method). For
* example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
* will be `data.someProp`.
*
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend
* the default set of resource actions. The declaration should be created in the format of {@link
+1 -1
View File
@@ -104,7 +104,7 @@
*/
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
var LINKY_URL_REGEXP =
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/,
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"]/,
MAILTO_REGEXP = /^mailto:/;
return function(text, target) {
+7
View File
@@ -232,6 +232,13 @@ function makeMap(str) {
* @param {object} handler
*/
function htmlParser( html, handler ) {
if (typeof html !== 'string') {
if (html === null || typeof html === 'undefined') {
html = '';
} else {
html = '' + html;
}
}
var index, chars, match, stack = [], last = html, text;
stack.last = function() { return stack[ stack.length - 1 ]; };
+12 -1
View File
@@ -174,6 +174,12 @@ describe('angular', function() {
expect(aCopy).toBe(aCopy.self);
expect(aCopy.selfs[2]).not.toBe(a.selfs[2]);
});
it('should clear destination arrays correctly when source is non-array', function() {
expect(copy(null, [1,2,3])).toEqual([]);
expect(copy(undefined, [1,2,3])).toEqual([]);
expect(copy({0: 1, 1: 2}, [1,2,3])).toEqual([1,2]);
});
});
describe("extend", function() {
@@ -360,6 +366,11 @@ describe('angular', function() {
expect(equals(new Date(0), new Date(1))).toBe(false);
expect(equals(new Date(0), 0)).toBe(false);
expect(equals(0, new Date(0))).toBe(false);
expect(equals(new Date(undefined), new Date(undefined))).toBe(true);
expect(equals(new Date(undefined), new Date(0))).toBe(false);
expect(equals(new Date(undefined), new Date(null))).toBe(false);
expect(equals(new Date(undefined), new Date('wrong'))).toBe(true);
});
it('should correctly test for keys that are present on Object.prototype', function() {
@@ -822,7 +833,7 @@ describe('angular', function() {
expect(function () {
angular.bootstrap(element);
}).toThrowMatching(
/\[ng:btstrpd\] App Already Bootstrapped with this Element '<div class="?ng\-scope"?( ng[0-9]+="?[0-9]+"?)?>'/i
/\[ng:btstrpd\] App Already Bootstrapped with this Element '&lt;div class="?ng\-scope"?( ng[0-9]+="?[0-9]+"?)?&gt;'/i
);
dealoc(element);
+21 -1
View File
@@ -1647,9 +1647,11 @@ describe('jqLite', function() {
element.triggerHandler('click');
event = pokeSpy.mostRecentCall.args[0];
expect(event.preventDefault).toBeDefined();
expect(event.target).toEqual(element[0]);
expect(event.type).toEqual('click');
});
it('should pass data as an additional argument', function() {
it('should pass extra parameters as an additional argument', function() {
var element = jqLite('<a>poke</a>'),
pokeSpy = jasmine.createSpy('poke'),
data;
@@ -1680,6 +1682,24 @@ describe('jqLite', function() {
expect(clickOnceSpy).toHaveBeenCalledOnce();
expect(clickSpy.callCount).toBe(2);
});
it("should accept a custom event instead of eventName", function() {
var element = jqLite('<a>poke</a>'),
pokeSpy = jasmine.createSpy('poke'),
customEvent = {
type: 'click',
someProp: 'someValue'
},
actualEvent;
element.on('click', pokeSpy);
element.triggerHandler(customEvent);
actualEvent = pokeSpy.mostRecentCall.args[0];
expect(actualEvent.preventDefault).toBeDefined();
expect(actualEvent.someProp).toEqual('someValue');
expect(actualEvent.target).toEqual(element[0]);
expect(actualEvent.type).toEqual('click');
});
});
+22
View File
@@ -2671,6 +2671,28 @@ describe('$compile', function() {
});
it('should update parent scope when "="-bound NaN changes', inject(function($compile, $rootScope) {
$rootScope.num = NaN;
compile('<div my-component reference="num"></div>');
var isolateScope = element.isolateScope();
expect(isolateScope.reference).toBeNaN();
isolateScope.$apply(function(scope) { scope.reference = 64; });
expect($rootScope.num).toBe(64);
}));
it('should update isolate scope when "="-bound NaN changes', inject(function($compile, $rootScope) {
$rootScope.num = NaN;
compile('<div my-component reference="num"></div>');
var isolateScope = element.isolateScope();
expect(isolateScope.reference).toBeNaN();
$rootScope.$apply(function(scope) { scope.num = 64; });
expect(isolateScope.reference).toBe(64);
}));
describe('attribute', function() {
it('should copy simple attribute', inject(function() {
compile('<div><span my-component attr="some text">');
+17
View File
@@ -252,6 +252,23 @@ describe('ngHref', function() {
expect(element.attr('href')).toEqual('http://server');
}));
it('should not set the href if ng-href is empty', inject(function($rootScope, $compile) {
$rootScope.url = null;
element = $compile('<a ng-href="{{url}}"></a>')($rootScope);
$rootScope.$digest();
expect(element.attr('href')).toEqual(undefined);
}));
it('should remove the href if ng-href changes to empty', inject(function($rootScope, $compile) {
$rootScope.url = 'http://www.google.com/';
element = $compile('<a ng-href="{{url}}"></a>')($rootScope);
$rootScope.$digest();
$rootScope.url = null;
$rootScope.$digest();
expect(element.attr('href')).toEqual(undefined);
}));
if (isDefined(window.SVGElement)) {
describe('SVGAElement', function() {
it('should interpolate the expression and bind to xlink:href', inject(function($compile, $rootScope) {
+24
View File
@@ -1546,6 +1546,30 @@ describe('input', function() {
expect(scope.items[0].selected).toBe(false);
});
});
describe('password', function() {
// Under no circumstances should input[type=password] trim inputs
it('should not trim if ngTrim is unspecified', function() {
compileInput('<input type="password" ng-model="password">');
changeInputValueTo(' - - untrimmed - - ');
expect(scope.password.length).toBe(' - - untrimmed - - '.length);
});
it('should not trim if ngTrim !== false', function() {
compileInput('<input type="password" ng-model="password" ng-trim="true">');
changeInputValueTo(' - - untrimmed - - ');
expect(scope.password.length).toBe(' - - untrimmed - - '.length);
});
it('should not trim if ngTrim === false', function() {
compileInput('<input type="password" ng-model="password" ng-trim="false">');
changeInputValueTo(' - - untrimmed - - ');
expect(scope.password.length).toBe(' - - untrimmed - - '.length);
});
});
});
describe('NgModel animations', function() {
+123
View File
@@ -735,6 +735,8 @@ describe('select', function() {
it('should not update selected property of an option element on digest with no change event',
function() {
// ng-options="value.name for value in values"
// ng-model="selected"
createSingleSelect();
scope.$apply(function() {
@@ -743,6 +745,11 @@ describe('select', function() {
});
var options = element.find('option');
expect(scope.selected).toEqual({ name: 'A' });
expect(options.eq(0).prop('selected')).toBe(true);
expect(options.eq(1).prop('selected')).toBe(false);
var optionToSelect = options.eq(1);
expect(optionToSelect.text()).toBe('B');
@@ -987,6 +994,56 @@ describe('select', function() {
expect(element.find('option').eq(0).prop('selected')).toBeTruthy();
expect(element.find('option').length).toEqual(2);
});
it('should ensure that at least one option element has the "selected" attribute', function() {
function countSelected() {
var count = 0;
forEach(element.find('option'), function(option) {
count += option.getAttribute('selected') ? 1 : 0;
});
return count;
}
createSelect({
'ng-model': 'selected',
'ng-options': 'item.id as item.name for item in values'
});
scope.$apply(function() {
scope.values = [{id: 10, name: 'A'}, {id: 20, name: 'B'}];
});
expect(element.val()).toEqual('?');
expect(countSelected()).toEqual(1);
scope.$apply(function() {
scope.selected = 10;
});
// Here the ? option should disappear and the first real option should have selected attribute
expect(element.val()).toEqual('0');
expect(countSelected()).toEqual(1);
// Here the selected value is changed but we don't change the selected attribute
scope.$apply(function() {
scope.selected = 20;
});
expect(element.val()).toEqual('1');
expect(countSelected()).toEqual(1);
scope.$apply(function() {
scope.values.push({id: 30, name: 'C'});
});
expect(element.val()).toEqual('1');
expect(countSelected()).toEqual(1);
// Here the ? option should reappear and have selected attribute
scope.$apply(function() {
scope.selected = undefined;
});
expect(element.val()).toEqual('?');
expect(countSelected()).toEqual(1);
});
});
@@ -1134,8 +1191,73 @@ describe('select', function() {
browserTrigger(element, 'change');
expect(scope.selected).toEqual(null);
});
// Regression https://github.com/angular/angular.js/issues/7855
it('should update the model with ng-change', function() {
createSelect({
'ng-change':'change()',
'ng-model':'selected',
'ng-options':'value for value in values'
});
scope.$apply(function() {
scope.values = ['A', 'B'];
scope.selected = 'A';
});
scope.change = function() {
scope.selected = 'A';
};
element.find('option')[1].selected = true;
browserTrigger(element, 'change');
expect(element.find('option')[0].selected).toBeTruthy();
expect(scope.selected).toEqual('A');
});
});
describe('disabled blank', function() {
it('should select disabled blank by default', function() {
var html = '<select ng-model="someModel" ng-options="c for c in choices">' +
'<option value="" disabled>Choose One</option>' +
'</select>';
scope.$apply(function() {
scope.choices = ['A', 'B', 'C'];
});
compile(html);
var options = element.find('option');
var optionToSelect = options.eq(0);
expect(optionToSelect.text()).toBe('Choose One');
expect(optionToSelect.prop('selected')).toBe(true);
expect(element[0].value).toBe('');
dealoc(element);
});
it('should select disabled blank by default when select is required', function() {
var html = '<select ng-model="someModel" ng-options="c for c in choices" required>' +
'<option value="" disabled>Choose One</option>' +
'</select>';
scope.$apply(function() {
scope.choices = ['A', 'B', 'C'];
});
compile(html);
var options = element.find('option');
var optionToSelect = options.eq(0);
expect(optionToSelect.text()).toBe('Choose One');
expect(optionToSelect.prop('selected')).toBe(true);
expect(element[0].value).toBe('');
dealoc(element);
});
});
describe('select-many', function() {
@@ -1183,6 +1305,7 @@ describe('select', function() {
expect(scope.selected).toEqual([scope.values[0]]);
});
it('should select from object', function() {
createSelect({
'ng-model':'selected',
+12
View File
@@ -1217,6 +1217,18 @@ describe('$http', function() {
expect(callback.mostRecentCall.args[0]).toBe('content');
}));
it('should cache JSONP request when cache is provided', inject(function($rootScope) {
$httpBackend.expect('JSONP', '/url?cb=JSON_CALLBACK').respond('content');
$http({method: 'JSONP', url: '/url?cb=JSON_CALLBACK', cache: cache});
$httpBackend.flush();
$http({method: 'JSONP', url: '/url?cb=JSON_CALLBACK', cache: cache}).success(callback);
$rootScope.$digest();
expect(callback).toHaveBeenCalledOnce();
expect(callback.mostRecentCall.args[0]).toBe('content');
}));
it('should cache request when cache is provided and no method specified', function () {
doFirstCacheRequest();
+66 -6
View File
@@ -811,15 +811,21 @@ describe('$location', function() {
attrs = attrs ? ' ' + attrs + ' ' : '';
// fake the base behavior
if (!relLink) {
if (linkHref[0] == '/') {
linkHref = 'http://host.com' + linkHref;
} else if(!linkHref.match(/:\/\//)) {
linkHref = 'http://host.com/base/' + linkHref;
if (typeof linkHref === 'string') {
if (!relLink) {
if (linkHref[0] == '/') {
linkHref = 'http://host.com' + linkHref;
} else if(!linkHref.match(/:\/\//)) {
linkHref = 'http://host.com/base/' + linkHref;
}
}
}
link = jqLite('<a href="' + linkHref + '"' + attrs + '>' + content + '</a>')[0];
if (linkHref) {
link = jqLite('<a href="' + linkHref + '"' + attrs + '>' + content + '</a>')[0];
} else {
link = jqLite('<a ' + attrs + '>' + content + '</a>')[0];
}
$provide.value('$sniffer', {history: supportHist});
$locationProvider.html5Mode(html5Mode);
@@ -934,6 +940,34 @@ describe('$location', function() {
});
// Regression (gh-7721)
it('should not throw when clicking anchor with no href attribute when history enabled on old browser', function() {
configureService(null, true, false);
inject(
initBrowser(),
initLocation(),
function($browser) {
browserTrigger(link, 'click');
expectNoRewrite($browser);
}
);
});
it('should produce relative paths correctly when $location.path() is "/" when history enabled on old browser', function() {
configureService('partial1', true, false, true);
inject(
initBrowser(),
initLocation(),
function($browser, $location) {
$location.path('/');
browserTrigger(link, 'click');
expectRewriteTo($browser, 'http://host.com/base/index.html#!/partial1');
}
);
});
it('should rewrite abs link to hashbang url when history enabled on old browser', function() {
configureService('/base/link?a#b', true, false);
inject(
@@ -986,6 +1020,32 @@ describe('$location', function() {
});
it('should not rewrite links with `javascript:` URI', function() {
configureService(' jAvAsCrIpT:throw new Error("Boom!")', true, true, true);
inject(
initBrowser(),
initLocation(),
function($browser) {
browserTrigger(link, 'click');
expectNoRewrite($browser);
}
);
});
it('should not rewrite links with `mailto:` URI', function() {
configureService(' mAiLtO:foo@bar.com', true, true, true);
inject(
initBrowser(),
initLocation(),
function($browser) {
browserTrigger(link, 'click');
expectNoRewrite($browser);
}
);
});
it('should rewrite full url links to same domain and base path', function() {
configureService('http://host.com/base/new', true);
inject(
+27
View File
@@ -434,6 +434,17 @@ describe('parser', function() {
expect(scope.b).toEqual(234);
});
it('should evaluate assignments in ternary operator', function() {
scope.$eval('a = 1 ? 2 : 3');
expect(scope.a).toBe(2);
scope.$eval('0 ? a = 2 : a = 3');
expect(scope.a).toBe(3);
scope.$eval('1 ? a = 2 : a = 3');
expect(scope.a).toBe(2);
});
it('should evaluate function call without arguments', function() {
scope['const'] = function(a,b){return 123;};
expect(scope.$eval("const()")).toEqual(123);
@@ -1089,6 +1100,22 @@ describe('parser', function() {
fn.assign(scope, 123);
expect(scope).toEqual({a:123});
}));
it('should expose working assignment function for expressions ending with brackets', inject(function($parse) {
var fn = $parse('a.b["c"]');
expect(fn.assign).toBeTruthy();
var scope = {};
fn.assign(scope, 123);
expect(scope.a.b.c).toEqual(123);
}));
it('should expose working assignment function for expressions with brackets in the middle', inject(function($parse) {
var fn = $parse('a["b"].c');
expect(fn.assign).toBeTruthy();
var scope = {};
fn.assign(scope, 123);
expect(scope.a.b.c).toEqual(123);
}));
});
+7 -1
View File
@@ -30,6 +30,12 @@ describe('sanitizeUri', function() {
expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:doEvilStuff()');
});
it('should sanitize javascript: urls with comments', function() {
/* jshint scripturl:true */
testUrl = "javascript:alert(1)//data:image/";
expect(sanitizeImg(testUrl)).toBe('unsafe:javascript:alert(1)//data:image/');
});
it('should sanitize non-image data: urls', function() {
testUrl = "data:application/javascript;charset=US-ASCII,alert('evil!');";
expect(sanitizeImg(testUrl)).toBe("unsafe:data:application/javascript;charset=US-ASCII,alert('evil!');");
@@ -235,4 +241,4 @@ describe('sanitizeUri', function() {
});
});
});
+2
View File
@@ -25,6 +25,8 @@ describe('linky', function() {
toEqual('<a href="mailto:me@example.com">me@example.com</a>');
expect(linky("send email to me@example.com, but")).
toEqual('send email to <a href="mailto:me@example.com">me@example.com</a>, but');
expect(linky("my email is \"me@example.com\"")).
toEqual('my email is &#34;<a href="mailto:me@example.com">me@example.com</a>&#34;');
});
it('should handle target:', function() {
+10
View File
@@ -228,6 +228,16 @@ describe('HTML', function() {
.toEqual('<p> 10 &lt; <span>100</span> </p>');
});
it('should accept non-string arguments', function() {
expectHTML(null).toBe('');
expectHTML(undefined).toBe('');
expectHTML(42).toBe('42');
expectHTML({}).toBe('[object Object]');
expectHTML([1, 2, 3]).toBe('1,2,3');
expectHTML(true).toBe('true');
expectHTML(false).toBe('false');
});
describe('htmlSanitizerWriter', function() {
/* global htmlSanitizeWriter: false */
if (angular.isUndefined(window.htmlSanitizeWriter)) return;