Compare commits

..

69 Commits

Author SHA1 Message Date
Peter Bacon Darwin b879223cf3 fix(input): ensure that hidden input values are correct after history.back
Due to the nature of some browser's PageCache/BFCache, returning to an Angular
app sometimes causes `input[hidden]` elements to retain the last value
that was stored before the page was navigated away from previously.

This is particularly problematic if the input has an interpolated value.
E.g. `<input type="hidden" value="{{ 1 + 2 }}">` since when the browser
returns, instead of the original interpolation template, the HTML contains
the previous value `<input type="hidden" value="3">`.

This commit instructs the browser not to attempt to reinstate the previous
value when navigating back in history by setting `autocomplete="off"` on
the hidden input element element.
2016-10-10 22:22:30 +01:00
Georgios Kalpakas bba6004147 docs(ngModel): rename $asyncValidators error to nopromise and add missing error page
Closes #13795
2016-01-19 16:11:44 +02:00
Matias Niemelä d386b7ae4b fix(ngAnimate): do not use event.timeStamp anymore for time tracking
Due to recent changes in Chrome, Firefox and Webkit use of the
event.timeStamp value will lead to unpredictable behaviour due to
precision changes. Therefore it's best to stick entirely to use
`Date.now()` when it comes to confirming the end of transition-
ending values. See #13494 for more info.

Applies to 1.2, 1.3, 1.4 and 1.5.

Closes #13494
Closes #13495
2016-01-05 12:55:16 +00:00
Peter Bacon Darwin ebc5a477db chore(npm-shrinkwrap): update protractor dependency 2015-12-18 09:38:34 +00:00
Peter Bacon Darwin 21c935a303 chore(Gruntfile): replace double quotes with single quotes 2015-12-17 22:29:29 +00:00
Peter Bacon Darwin 7e5ef766da chore(GruntFile): fix whitespace in lists 2015-12-17 22:29:29 +00:00
Peter Bacon Darwin 1cb96303e9 chore(GruntFile): move validate-angular-files task into its own file
Closes #13569
2015-12-17 22:29:28 +00:00
Matias Niemelä d2058d92bd chore(build): add a validation step for angularFiles
Closes #13553
2015-12-17 22:29:28 +00:00
Peter Bacon Darwin 053f4e26ae chore(angularFiles): add documentation only file to list of files
This prevents errors when checking `validate-angular-files`
2015-12-17 22:29:28 +00:00
Peter Bacon Darwin 45ca45a29a chore(npm-shrinkwrap): install glob package 2015-12-17 22:29:25 +00:00
Peter Bacon Darwin e2bf5726ab chore(jenkins): remove unused argument definition 2015-12-17 14:15:38 +00:00
Peter Bacon Darwin bf706f2f52 chore(jenkins): run Jenkins builds on Node 4 (via nvm)
Closes #13568
2015-12-17 14:11:09 +00:00
Peter Bacon Darwin d2fe381ea8 chore(jenkins): move jenkins_build.sh to scripts/jenkins/build.sh 2015-12-16 14:23:12 +00:00
Peter Bacon Darwin 63c83ffa43 chore(travis): update to use node 4.x 2015-12-16 11:15:48 +00:00
Martin Staffa 7fa13c3da2 chore(travis): add a new job that runs ci-checks
Previously, ddescribe, merge-conflicts, jshint, and jscs would run
after unit & e2e tests ran. The order was orginally changed as part of
https://github.com/angular/angular.js/pull/9792.

While the logic is sound that style errors shouldn't block tests from
running, ddescribe should always run. This was not guaraneteed; when
Travis exits with a warning after some browsers have run, ddescribe
doesn't get run and it doesn't become apparent that not
all tests have run.

Additionally, a separate job clearly separates style from test errors,
which e.g. means you can open a PR that includes an iit to speed up
the job, and see immediately if the test passes, because the ddescribe
error is in another job.
2015-12-16 10:38:12 +00:00
Georgios Kalpakas 4b91042559 docs(form): remove mention of interpolated control names not being supported
The docs state that interpolation cannot be used in control names.
This used to be true, but not anymore.

Closes #13520
2015-12-15 20:05:18 +02:00
Jason Bedard a9ecde1e33 perf(copy): avoid regex in isTypedArray
Closes: #12054
2015-11-19 10:00:37 +00:00
Jason Bedard ada25a9266 perf(copy): only validate/clear user specified destination
Closes #12068
2015-11-19 10:00:22 +00:00
Peter Bacon Darwin 0f956b2893 fix(angular.copy): support copying XML nodes
Closes #5429
Closes #12786
2015-11-19 09:59:44 +00:00
Jason Bedard 17eb3d717f fix(copy): do not copy the same object twice 2015-11-19 09:59:19 +00:00
Peter Bacon Darwin c9ccc801b9 fix(copy): support copying properties with a null prototype
Partially cherry-picked from f7b999703f
2015-11-19 09:58:24 +00:00
Justin Schiff 420490aa1b fix(angular.copy): support copying %TypedArray%s
angular.copy can now copy a %TypedArray%s.

Limitations: It is not possible to update the length of a %TypedArray%, so currently an error is thrown
if the destination object is a %TypedArray%. However, it is possible to change values in a typed array,
so in the future this may only be a problem if the length of the source and destination is different.

Closes #10745
2015-11-19 09:46:07 +00:00
Matias Niemelä 2a65c3deb7 chore(CHANGELOG): update with changes for 1.3.20 2015-09-29 13:54:03 -07:00
Igor Minar ef0c333776 build(travis): make sauce connect process query a bit more specific 2015-09-23 14:01:51 -07:00
Georgios Kalpakas bce1d8ecad chore(check-node-modules): make check/reinstall node_modules work across platforms
The previous implementations (based on shell scripts) threw errors on
Windows, because it was not able to `rm -rf` 'node_modules' (due to the
255 character limit in file-paths).

This implementation works consistently across platforms and is heavily based on
'https://github.com/angular/angular/blob/3b9c08676a4c921bbfa847802e08566fb601ba7a/tools/npm/check-node-modules.js'.

Fixes #11143
Closes #11353

Closes #12792
2015-09-23 23:25:04 +03:00
Igor Minar 5a1bc6eb32 build(travis): fix typo in a comment 2015-09-23 11:00:44 -07:00
Igor Minar 1f7a0b2b72 build(travis): gracefully shut down the sauce connect tunnel after the tests are done running
This is to prevent sauce connect tunnel leaks.

Closes #12921
2015-09-23 09:40:46 -07:00
Lucas Mirelmann a665550933 test($parse): fix test for Firefox and IE 2015-09-20 19:54:37 +02:00
Lucas Mirelmann d434f3db53 fix($parse): do not convert to string computed properties multiple times
Do not convert to string properties multiple times.
2015-09-20 13:39:33 +02:00
Peter Bacon Darwin 7e08975b67 chore(travis): upgrade Travis builds to use container infrastructure 2015-09-17 12:31:52 +01:00
Magee Mooney 82c481bb23 docs(gdocs.js): fix typo (Eror -> Error)
Closes #12858
2015-09-16 23:11:39 +01:00
Peter Bacon Darwin b1f46bb1b2 chore(bower/publish): move DIST_TAG so that it gets the correct value
In the position that DIST_TAG was being assigned it was trying to get the
`distTag` value from the wrong (i.e. a bower-...) repository.
2015-09-16 23:11:35 +01:00
Peter Bacon Darwin 3a6bf0d5bc revert: fix($compile): throw error on invalid directive name
This reverts commit 634e467172, which introduced
a breaking change between 1.3.15 and 1.3.16.

Closes #12169
2015-09-15 14:38:21 +01:00
Peter Bacon Darwin e201f9040f docs(CHANGELOG): update with 1.3.19 changes 2015-09-15 13:34:09 +01:00
Peter Bacon Darwin 40e9bcd1b4 chore(scripts/publish): get dist-tag from package.json
Closes #12722
2015-09-14 21:45:20 +01:00
Matias Niemelä f98e038418 feat(ngAnimate): introduce $animate.flush for unit testing 2015-09-14 13:08:57 -07:00
Lucas Galfaso ec98c94ccb fix($parse): throw error when accessing a restricted property indirectly
When accessing an instance thru a computed member and the property is an array,
then also check the string value of the array.

Closes #12833
2015-09-13 16:35:46 +01:00
Pawel Kozlowski f13055a0a5 fix($http): propagate status -1 for timed out requests
Fixes #4491
Closes #8756
2015-09-07 14:34:10 +01:00
Peter Bacon Darwin 623ce1ad2c fix($location): don't crash if navigating outside the app base
Previously, if you navigate outside of the Angular application, say be clicking
the back button, the $location service would try to handle the url change
and error due to the URL not being valid for the application.

This fixes that issue by ensuring that a reload happens when you navigate
to a URL that is not within the application.

Closes #11667
2015-09-07 14:33:17 +01:00
Peter Bacon Darwin 34cf141838 refactor($location): compute appBaseNoFile only once 2015-09-07 14:33:17 +01:00
Martin Staffa 274e93537e fix(ngModel): validate pattern against the viewValue
Since the HTML5 pattern validation constraint validates the input value,
we should also validate against the viewValue. While this worked in
core up to Angular 1.2, in 1.3, we changed not only validation,
but the way `input[date]` and `input[number]` are handled - they parse
their input values into `Date` and `Number` respectively, which cannot
be validated by a regex.

Fixes #12344

BREAKING CHANGE:

The `ngPattern` and `pattern` directives will validate the regex
against the `viewValue` of `ngModel`, i.e. the value of the model
before the $parsers are applied. Previously, the modelValue
(the result of the $parsers) was validated.

This fixes issues where `input[date]` and `input[number]` cannot
be validated because the viewValue string is parsed into
`Date` and `Number` respectively (starting with Angular 1.3).
It also brings the directives in line with HTML5 constraint
validation, which validates against the input value.

This change is unlikely to cause applications to fail, because even
in Angular 1.2, the value that was validated by pattern could have
been manipulated by the $parsers, as all validation was done
inside this pipeline.

If you rely on the pattern being validated against the modelValue,
you must create your own validator directive that overwrites
the built-in pattern validator:

```
.directive('patternModelOverwrite', function patternModelOverwriteDirective() {
  return {
    restrict: 'A',
    require: '?ngModel',
    priority: 1,
    compile: function() {
      var regexp, patternExp;

      return {
        pre: function(scope, elm, attr, ctrl) {
          if (!ctrl) return;

          attr.$observe('pattern', function(regex) {
            /**
             * The built-in directive will call our overwritten validator
             * (see below). We just need to update the regex.
             * The preLink fn guaranetees our observer is called first.
             */
            if (isString(regex) && regex.length > 0) {
              regex = new RegExp('^' + regex + '$');
            }

            if (regex && !regex.test) {
              //The built-in validator will throw at this point
              return;
            }

            regexp = regex || undefined;
          });

        },
        post: function(scope, elm, attr, ctrl) {
          if (!ctrl) return;

          regexp, patternExp = attr.ngPattern || attr.pattern;

          //The postLink fn guarantees we overwrite the built-in pattern validator
          ctrl.$validators.pattern = function(value) {
            return ctrl.$isEmpty(value) ||
              isUndefined(regexp) ||
              regexp.test(value);
          };
        }
      };
    }
  };
});
```
2015-08-28 10:57:07 +02:00
Matias Niemelä f7622dcc0d docs(CHANGELOG): add changes for 1.3.18 2015-08-18 15:14:56 -07:00
Matias Niemelä 2c03a35743 fix($animate): clear class animations cache if animation is not started
Closes #12604
Closes #12603
2015-08-17 17:09:00 -07:00
Matias Niemelä 6b72598b87 fix($animate): do not throw errors if element is removed before animation starts
Closes #10205
2015-08-17 17:06:54 -07:00
Rouven Weßling 51e24d75c3 refactor(): remove more bits and pieces related to Internet Explorer 8
Closes #12407
2015-08-08 18:08:47 +02:00
Martin Staffa 64a142b58e fix(ngModel): correct minErr usage for correct doc creation
Remove the `new` from the minErr assignment, so the closure compiler
can detect the errors correctly. Also removes the leading $ from the
variable name to be consistent with the Angular.js file.

Closes #12386
Closes #12416
2015-08-08 18:08:15 +02:00
Martin Staffa 0026ebf2de docs($rootScope.Scope): remove obsolete line, and link to guide
The removed line pointed to a removed example. Re-adding the example
would have been of questionable value, as it introduced several
concepts without context. It's therefore better to link to the guide,
which provides a better introduction.

Closes #12167
2015-08-03 22:07:59 +02:00
Eric Adams a4f73c9f99 docs(guide/Dependency Injection): fix angular.injector arguments list
The original file included a code sample using `angular.injector(['myModule', 'ng'])`,
which appears to be incorrect when trying to retrieve anything attached to `myModule`.
Reversing the args fixes this.

Closes #12292
2015-08-03 22:06:35 +02:00
Satish Maurya bd5c4e5f0e docs(guide/Forms): display scope form / master data in examples
It will be good to have the binding results in the CSS classes /
binding to form / control state example, similar to the Simple Form
example.

Closes #12326
2015-08-03 22:06:27 +02:00
Steven 9742565d61 docs(guide): Facebook was mispelled as Faceb0ok
Fixes typo :>

Closes #12470
2015-08-03 22:05:45 +02:00
Strikeskids 6f33dfa8cc docs($rootScope.Scope): improve clarity describing $watch with no listener
The previous explanation in parentheses created a bit of confusion because the documentation stated to leave off the `listener`, but then said "be prepared for multiple calls to your listener". The new explanation clarifies that it is indeed the `watchExpression` that will be executed multiple times.

Closes #12429
2015-08-03 22:05:36 +02:00
Laisky.Cai 96f0c8df17 docs(guide/expression): replace tt by code
Replaces <tt> elements with <code> in expressions guide. Looks identical
in Chromium

Closes #12437

Conflicts:
	docs/content/guide/expression.ngdoc
2015-08-03 22:04:40 +02:00
Martin Staffa 53fb534889 docs(ngOptions): remove obsolete trkslct error page
Closes #12417
2015-08-03 22:02:33 +02:00
Blake Johnston 6271ac064c docs($compile): pluralize DOM element
Previous description includes singular `collection of DOM element`. Current change revises `element` to be plural.

Closes #12431
2015-08-03 22:02:25 +02:00
ColinFletch 2d71b5b053 docs(guide/Controllers): Syntax adjustments.
Closes #12379
2015-08-03 22:02:13 +02:00
Jesse Mandel a547dff09b docs(guide/module): fixed link to blog post 2015-07-19 16:38:37 +02:00
Andrew Passanisi 3f9517ea5b docs(error/ctrlfmt): fixed a small typo in ctrl error message
Closes #12320
2015-07-16 22:45:47 +02:00
Mohamed Samy 8b4ffdde7a docs(tutorial/7 - Routing): fix matching in test
It is corrected in github, but not in the angular.org site.
Copied it from https://github.com/angular/angular-phonecat/compare/step-6...step-7

Closes #12314
2015-07-16 22:45:40 +02:00
Nabil Kadimi e57240d87d docs(guide/Dependency Injection): minor punctuation fixes
Closes #12268
2015-07-16 22:45:29 +02:00
shoja a51b4e6dc4 docs($sce): correct typos
Line 548: Remove duplicate 'not' and clarify wording
Line 556: Remove period within parenthetical statement
Line 560: Clarify wording
Line 570: Capitalize 'E.g.' at the start of a sentence

Closes #12252
2015-07-16 22:45:21 +02:00
Steve Mao 215be0b0ab docs(CONTRIBUTING): revert is a modifier
EG: https://github.com/angular/angular.js/commit/462f444b06ae5cad3ccb761b1dba7131df01a655

Closes #12032
2015-07-13 13:26:45 +01:00
Steve Mao 7c5880f998 docs(CONTRIBUTING): state what is mandatory or optional
Closes #12032
2015-07-13 13:25:45 +01:00
Steve Mao 0d941a986a docs(CONTRIBUTING): how to write a breaking change
Closes #12032
2015-07-13 13:25:45 +01:00
Peter Bacon Darwin 8714cabdb7 docs(guide/controller): add a line about controller as 2015-07-13 13:22:28 +01:00
Peter Bacon Darwin cbab2923ef docs(guide/controller): add a line about controller as 2015-07-13 13:20:28 +01:00
niteshthakur dba7e20e96 docs(guide/controller): clarify that controllers are defined **by** a constructor function
A controller is a instantiated object created **from** a constructor function.
It was not accurate to describe a Controller **as** a constructor function.

Closes #11888
2015-07-13 13:20:28 +01:00
Peter Bacon Darwin 24dd9ea649 docs($routeChangeSuccess): note that resolve values are available on current route
Closes #11413
2015-07-13 13:11:15 +01:00
Rouven Weßling 8bd59a593b refactor(ngCsp): use document.head
The `head` property is available from IE9 onwards.

Closes #11905
2015-07-13 10:07:52 +01:00
Martin Staffa 9a1349d2e0 docs(CHANGLOG): add changes for 1.3.17 2015-07-06 22:22:45 +02:00
71 changed files with 2409 additions and 984 deletions
+17 -21
View File
@@ -1,6 +1,7 @@
language: node_js
sudo: false
node_js:
- '0.10'
- '4.2'
cache:
directories:
@@ -14,28 +15,26 @@ branches:
env:
matrix:
- JOB=ci-checks
- JOB=unit BROWSER_PROVIDER=saucelabs
- JOB=docs-e2e BROWSER_PROVIDER=saucelabs
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=saucelabs
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=saucelabs
- JOB=unit BROWSER_PROVIDER=browserstack
- JOB=docs-e2e BROWSER_PROVIDER=browserstack
- JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack
- JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack
global:
- CXX=g++-4.8 # node 4 likes the G++ v4.8 compiler
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
- BROWSER_STACK_USERNAME=VojtaJina
- BROWSER_STACK_ACCESS_KEY=QCQJ1ZpWXpBkSwEdD8ev
- LOGS_DIR=/tmp/angular-build/logs
- BROWSER_PROVIDER_READY_FILE=/tmp/browsersprovider-tunnel-ready
matrix:
allow_failures:
- env: "JOB=unit BROWSER_PROVIDER=browserstack"
- env: "JOB=docs-e2e BROWSER_PROVIDER=browserstack"
- env: "JOB=e2e TEST_TARGET=jqlite BROWSER_PROVIDER=browserstack"
- env: "JOB=e2e TEST_TARGET=jquery BROWSER_PROVIDER=browserstack"
# node 4 likes the G++ v4.8 compiler
# see https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
install:
# Check the size of caches
@@ -45,21 +44,18 @@ install:
- npm config set spin false
# Log HTTP requests
- npm config set loglevel http
- npm install -g npm@2.5
# Instal npm dependecies and ensure that npm cache is not stale
- scripts/npm/install-dependencies.sh
#- npm install -g npm@2.5
# Install npm dependencies and ensure that npm cache is not stale
- npm install
before_script:
- mkdir -p $LOGS_DIR
- ./scripts/travis/start_browser_provider.sh
- npm install -g grunt-cli
- grunt package
- ./scripts/travis/wait_for_browser_provider.sh
- ./scripts/travis/before_build.sh
script:
- ./scripts/travis/build.sh
after_script:
- ./scripts/travis/tear_down_browser_provider.sh
- ./scripts/travis/print_logs.sh
notifications:
+157
View File
@@ -1,3 +1,160 @@
<a name="1.3.20"></a>
# 1.3.20 shallow-translucence (2015-09-29)
## Bug Fixes
- **$parse:** do not convert to string computed properties multiple times
([d434f3db](https://github.com/angular/angular.js/commit/d434f3db53d6209eb140b904e83bbde401686c16))
## Breaking Changes
<a name="1.3.19"></a>
# 1.3.19 glutinous-shriek (2015-09-15)
## Bug Fixes
- **$http:** propagate status -1 for timed out requests
([f13055a0](https://github.com/angular/angular.js/commit/f13055a0a53a39b160448713a5617edee6042801),
[#4491](https://github.com/angular/angular.js/issues/4491), [#8756](https://github.com/angular/angular.js/issues/8756))
- **$location:** don't crash if navigating outside the app base
([623ce1ad](https://github.com/angular/angular.js/commit/623ce1ad2cf68024719c5cae5d682d00195df30c),
[#11667](https://github.com/angular/angular.js/issues/11667))
- **$parse:** throw error when accessing a restricted property indirectly
([ec98c94c](https://github.com/angular/angular.js/commit/ec98c94ccbfc97b655447956738d5f6ff98b2f33),
[#12833](https://github.com/angular/angular.js/issues/12833))
- **ngModel:** validate pattern against the viewValue
([274e9353](https://github.com/angular/angular.js/commit/274e93537ed4e95aefeacea48909eb334894f0ac),
[#12344](https://github.com/angular/angular.js/issues/12344))
## Features
- **ngAnimate:** introduce `$animate.flush` for unit testing
([f98e0384](https://github.com/angular/angular.js/commit/f98e038418f7367b2373adcf4887f64a8e8bdcb0))
## Possible Breaking Changes
- **ngModel:** due to [274e9353](https://github.com/angular/angular.js/commit/274e93537ed4e95aefeacea48909eb334894f0ac),
The `ngPattern` and `pattern` directives will validate the regex
against the `viewValue` of `ngModel`, i.e. the value of the model
before the $parsers are applied. Previously, the modelValue
(the result of the $parsers) was validated.
This fixes issues where `input[date]` and `input[number]` cannot
be validated because the viewValue string is parsed into
`Date` and `Number` respectively (starting with Angular 1.3).
It also brings the directives in line with HTML5 constraint
validation, which validates against the input value.
This change is unlikely to cause applications to fail, because even
in Angular 1.2, the value that was validated by pattern could have
been manipulated by the $parsers, as all validation was done
inside this pipeline.
If you rely on the pattern being validated against the modelValue,
you must create your own validator directive that overwrites
the built-in pattern validator:
```
.directive('patternModelOverwrite', function patternModelOverwriteDirective() {
return {
restrict: 'A',
require: '?ngModel',
priority: 1,
compile: function() {
var regexp, patternExp;
return {
pre: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
attr.$observe('pattern', function(regex) {
/**
* The built-in directive will call our overwritten validator
* (see below). We just need to update the regex.
* The preLink fn guaranetees our observer is called first.
*/
if (isString(regex) && regex.length > 0) {
regex = new RegExp('^' + regex + '$');
}
if (regex && !regex.test) {
//The built-in validator will throw at this point
return;
}
regexp = regex || undefined;
});
},
post: function(scope, elm, attr, ctrl) {
if (!ctrl) return;
regexp, patternExp = attr.ngPattern || attr.pattern;
//The postLink fn guarantees we overwrite the built-in pattern validator
ctrl.$validators.pattern = function(value) {
return ctrl.$isEmpty(value) ||
isUndefined(regexp) ||
regexp.test(value);
};
}
};
}
};
});
```
<a name="1.3.18"></a>
# 1.3.18 collective-penmanship (2015-08-18)
## Bug Fixes
- **$animate:**
- clear class animations cache if animation is not started
([2c03a357](https://github.com/angular/angular.js/commit/2c03a3574336ed814d020cf7ba36cee5b87e65b5),
[#12604](https://github.com/angular/angular.js/issues/12604), [#12603](https://github.com/angular/angular.js/issues/12603))
- do not throw errors if element is removed before animation starts
([6b72598b](https://github.com/angular/angular.js/commit/6b72598b87022e1dd96bddc4451e007ef0601579),
[#10205](https://github.com/angular/angular.js/issues/10205))
- **ngModel:** correct minErr usage for correct doc creation
([64a142b5](https://github.com/angular/angular.js/commit/64a142b58ed0a0e3896d82f3f9ce35373548d0ff),
[#12386](https://github.com/angular/angular.js/issues/12386), [#12416](https://github.com/angular/angular.js/issues/12416))
<a name="1.3.17"></a>
# 1.3.17 tsktskskly-euouae (2015-07-06)
## Bug Fixes
- **$browser:** prevent infinite digest if changing hash when there is no hashPrefix
([61a3fb67](https://github.com/angular/angular.js/commit/61a3fb676a186e22564fb0181c17647b35ca4e5e),
[#10423](https://github.com/angular/angular.js/issues/10423), [#12145](https://github.com/angular/angular.js/issues/12145))
- **$location:**
- allow navigating outside the original base URL
([0bb57d53](https://github.com/angular/angular.js/commit/0bb57d538f25a1b6f20025d87a451c39671b59aa),
[#11302](https://github.com/angular/angular.js/issues/11302), [#4776](https://github.com/angular/angular.js/issues/4776))
- do not get caught in infinite digest in IE9
([f486ebe8](https://github.com/angular/angular.js/commit/f486ebe80b6d7854d3eb9029f14d94299cf493cb),
[#11439](https://github.com/angular/angular.js/issues/11439), [#11675](https://github.com/angular/angular.js/issues/11675), [#11935](https://github.com/angular/angular.js/issues/11935), [#12083](https://github.com/angular/angular.js/issues/12083))
- **linky:** allow case insensitive scheme detection
([6b28aef1](https://github.com/angular/angular.js/commit/6b28aef1c537bfb2da21820d6ca154344efe266e),
[#12073](https://github.com/angular/angular.js/issues/12073), [#12074](https://github.com/angular/angular.js/issues/12074))
<a name="1.3.16"></a>
# 1.3.16 cookie-oatmealification (2015-06-05)
+6
View File
@@ -199,9 +199,14 @@ format that includes a **type**, a **scope** and a **subject**:
<footer>
```
The **header** is mandatory and the **scope** of the header is optional.
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
to read on github as well as in various git tools.
### Revert
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
### Type
Must be one of the following:
@@ -235,6 +240,7 @@ The body should include the motivation for the change and contrast this with pre
The footer should contain any information about **Breaking Changes** and is also the place to
reference GitHub issues that this commit **Closes**.
**Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines. The rest of the commit message is then used for this.
A detailed explanation can be found in this [document][commit-message-format].
+14 -12
View File
@@ -155,7 +155,7 @@ module.exports = function(grunt) {
jscs: {
src: ['src/**/*.js', 'test/**/*.js'],
options: {
config: ".jscsrc"
config: '.jscsrc'
}
},
@@ -220,9 +220,9 @@ module.exports = function(grunt) {
dest: 'build/angular-aria.js',
src: util.wrap(files['angularModules']['ngAria'], 'module')
},
"promises-aplus-adapter": {
'promises-aplus-adapter': {
dest:'tmp/promises-aplus-adapter++.js',
src:['src/ng/q.js','lib/promises-aplus/promises-aplus-test-adapter.js']
src:['src/ng/q.js', 'lib/promises-aplus/promises-aplus-test-adapter.js']
}
},
@@ -241,7 +241,7 @@ module.exports = function(grunt) {
},
"ddescribe-iit": {
'ddescribe-iit': {
files: [
'src/**/*.js',
'test/**/*.js',
@@ -262,7 +262,7 @@ module.exports = function(grunt) {
}
},
"merge-conflict": {
'merge-conflict': {
files: [
'src/**/*',
'test/**/*',
@@ -292,11 +292,11 @@ module.exports = function(grunt) {
},
shell: {
"npm-install": {
command: path.normalize('scripts/npm/install-dependencies.sh')
'npm-install': {
command: 'node scripts/npm/check-node-modules.js'
},
"promises-aplus-tests": {
'promises-aplus-tests': {
options: {
stdout: false,
stderr: true,
@@ -327,8 +327,10 @@ module.exports = function(grunt) {
grunt.task.run('shell:npm-install');
}
//alias tasks
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'jscs', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:protractor']);
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'jscs', 'package', 'test:unit', 'test:promises-aplus', 'tests:docs', 'test:protractor']);
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
grunt.registerTask('test:jquery', 'Run the jQuery unit tests with Karma', ['tests:jquery']);
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['build', 'tests:modules']);
@@ -338,11 +340,11 @@ module.exports = function(grunt) {
grunt.registerTask('test:travis-protractor', 'Run the end to end tests with Protractor for Travis CI builds', ['connect:testserver', 'protractor:travis']);
grunt.registerTask('test:ci-protractor', 'Run the end to end tests with Protractor for Jenkins CI builds', ['webdriver', 'connect:testserver', 'protractor:jenkins']);
grunt.registerTask('test:e2e', 'Alias for test:protractor', ['test:protractor']);
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter', 'shell:promises-aplus-tests']);
grunt.registerTask('minify', ['bower','clean', 'build', 'minall']);
grunt.registerTask('minify', ['bower', 'clean', 'build', 'minall']);
grunt.registerTask('webserver', ['connect:devserver']);
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('package', ['bower', 'validate-angular-files', 'clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'jscs']);
grunt.registerTask('default', ['package']);
};
+2 -1
View File
@@ -32,6 +32,7 @@ var angularFiles = {
'src/ng/q.js',
'src/ng/raf.js',
'src/ng/rootScope.js',
'src/ng/rootElement.js',
'src/ng/sanitizeUri.js',
'src/ng/sce.js',
'src/ng/sniffer.js',
@@ -78,7 +79,7 @@ var angularFiles = {
],
'angularLoader': [
'stringify.js',
'src/stringify.js',
'src/minErr.js',
'src/loader.js'
],
-8
View File
@@ -1,8 +0,0 @@
@ngdoc error
@name $compile:baddir
@fullName Invalid Directive Name
@description
This error occurs when the name of a directive is not valid.
Directives must start with a lowercase character.
+1 -1
View File
@@ -11,7 +11,7 @@ Supported formats:
1. `__name__`
2. `__name__ as __identifier__`
N'either `__name__` or `__identifier__` may contain spaces.
Neither `__name__` or `__identifier__` may contain spaces.
Example of incorrect usage that leads to this error:
```html
+7
View File
@@ -0,0 +1,7 @@
@ngdoc error
@name ng:cpta
@fullName Copying TypedArray
@description
Copying TypedArray's with a destination is not supported because TypedArray
objects can not be mutated, they are fixed length.
@@ -0,0 +1,28 @@
@ngdoc error
@name ngModel:nopromise
@fullName No promise
@description
The return value of an async validator, must always be a promise. If you want to return a
non-promise value, you can convert it to a promise using {@link ng.$q#resolve `$q.resolve()`} or
{@link ng.$q#reject `$q.reject()`}.
Example:
```
.directive('asyncValidator', function($q) {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) {
ngModel.$asyncValidators.myAsyncValidation = function(modelValue, viewValue) {
if (/* I don't need to hit the backend API */) {
return $q.resolve(); // to mark as valid or
// return $q.reject(); // to mark as invalid
} else {
// ...send a request to the backend and return a promise
}
};
}
};
})
```
@@ -1,33 +0,0 @@
@ngdoc error
@name ngOptions:trkslct
@fullName Comprehension expression cannot contain both `select as` and `track by` expressions.
@description
NOTE: This error was introduced in 1.3.0-rc.5, and was removed for 1.3.0-rc.6 in order to
not break existing apps.
This error occurs when 'ngOptions' is passed a comprehension expression that contains both a
`select as` expression and a `track by` expression. These two expressions are fundamentally
incompatible.
* Example of bad expression: `<select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected">`
`values: [{id: 1, label: 'aLabel', subItem: {name: 'aSubItem'}}, {id: 2, label: 'bLabel', subItem: {name: 'bSubItem'}}]`,
`$scope.selected = {name: 'aSubItem'};`
* track by is always applied to `value`, with purpose to preserve the selection,
(to `item` in this case)
* To calculate whether an item is selected, `ngOptions` does the following:
1. apply `track by` to the values in the array:
In the example: [1,2]
2. apply `track by` to the already selected value in `ngModel`:
In the example: this is not possible, as `track by` refers to `item.id`, but the selected
value from `ngModel` is `{name: aSubItem}`.
Here's an example of how to make this example work by using `track by` without `select as`:
```
<select ng-model="selected" ng-options="item.label for item in values track by item.id">
```
Note: This would store the whole `item` as the model to `scope.selected` instead of `item.subItem`.
For more information on valid expression syntax, see 'ngOptions' in {@link ng.directive:select select} directive docs.
+9 -6
View File
@@ -5,13 +5,16 @@
# Understanding Controllers
In Angular, a Controller is a JavaScript **constructor function** that is used to augment the
In Angular, a Controller is defined by a JavaScript **constructor function** that is used to augment the
{@link scope Angular Scope}.
When a Controller is attached to the DOM via the {@link ng.directive:ngController ng-controller}
directive, Angular will instantiate a new Controller object, using the specified Controller's
**constructor function**. A new **child scope** will be available as an injectable parameter to the
Controller's constructor function as `$scope`.
**constructor function**. A new **child scope** will be created and made available as an injectable
parameter to the Controller's constructor function as `$scope`.
If the controller has been attached using the `controller as` syntax then the controller instance will
be assigned to a property on the new scope.
Use controllers to:
@@ -106,7 +109,7 @@ needed for a single view.
The most common way to keep Controllers slim is by encapsulating work that doesn't belong to
controllers into services and then using these services in Controllers via dependency injection.
This is discussed in the {@link di Dependency Injection} {@link services
This is discussed in the {@link di Dependency Injection} and {@link services
Services} sections of this guide.
@@ -162,7 +165,7 @@ scope is augmented (managed) by the `SpicyController` Controller.
starts with capital letter and ends with "Controller".
- Assigning a property to `$scope` creates or updates the model.
- Controller methods can be created through direct assignment to scope (see the `chiliSpicy` method)
- The Controller methods and properties are available in the template (for the `<div>` element and
- The Controller methods and properties are available in the template (for both the `<div>` element and
its children).
## Spicy Arguments Example
@@ -302,7 +305,7 @@ describe('myController function', function() {
```
If you need to test a nested Controller you need to create the same scope hierarchy
If you need to test a nested Controller you must create the same scope hierarchy
in your test that exists in the DOM:
```js
+3 -3
View File
@@ -163,8 +163,8 @@ someModule.controller('MyController', function($scope, greeter) {
});
```
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
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.
One advantage of this approach is that there's no array of names to keep in sync with the
@@ -293,7 +293,7 @@ Create a new injector that can provide components defined in our `myModule` modu
`greeter` service from the injector. (This is usually done automatically by angular bootstrap).
```js
var injector = angular.injector(['myModule', 'ng']);
var injector = angular.injector(['ng', 'myModule']);
var greeter = injector.get('greeter');
```
+2 -3
View File
@@ -70,7 +70,7 @@ You can try evaluating different expressions here:
<ul>
<li ng-repeat="expr in exprs track by $index">
[ <a href="" ng-click="removeExp($index)">X</a> ]
<tt>{{expr}}</tt> => <span ng-bind="$parent.$eval(expr)"></span>
<code>{{expr}}</code> => <span ng-bind="$parent.$eval(expr)"></span>
</li>
</ul>
</div>
@@ -344,5 +344,4 @@ When using a directive that takes an expression:
<ul>
<li ng-repeat="item in ::items">{{item.name}};</li>
</ul>
```
```
+4
View File
@@ -93,6 +93,8 @@ and failing to satisfy its validity.
<input type="button" ng-click="reset()" value="Reset" />
<input type="submit" ng-click="update(user)" value="Save" />
</form>
<pre>form = {{user | json}}</pre>
<pre>master = {{master | json}}</pre>
</div>
<style type="text/css">
@@ -181,6 +183,8 @@ didn't interact with a control
<input type="button" ng-click="reset(form)" value="Reset" />
<input type="submit" ng-click="update(user)" value="Save" />
</form>
<pre>form = {{user | json}}</pre>
<pre>master = {{master | json}}</pre>
</div>
</file>
+1 -1
View File
@@ -53,7 +53,7 @@ In Angular applications, you move the job of filling page templates with data fr
## Specific Topics
* **Login: **[Google example](https://developers.google.com/+/photohunt/python), [AngularJS Faceb0ok library](https://github.com/pc035860/angular-easyfb), [Facebook example](http://blog.brunoscopelliti.com/facebook-authentication-in-your-angularjs-web-app), [authentication strategy](http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app), [unix-style authorization](http://frederiknakstad.com/authentication-in-single-page-applications-with-angular-js/)
* **Login: **[Google example](https://developers.google.com/+/photohunt/python), [AngularJS Facebook library](https://github.com/pc035860/angular-easyfb), [Facebook example](http://blog.brunoscopelliti.com/facebook-authentication-in-your-angularjs-web-app), [authentication strategy](http://blog.brunoscopelliti.com/deal-with-users-authentication-in-an-angularjs-web-app), [unix-style authorization](http://frederiknakstad.com/authentication-in-single-page-applications-with-angular-js/)
* **Mobile:** [Angular on Mobile Guide](http://www.ng-newsletter.com/posts/angular-on-mobile.html), [PhoneGap](http://devgirl.org/2013/06/10/quick-start-guide-phonegap-and-angularjs/)
* **Other Languages:** [CoffeeScript](http://www.coffeescriptlove.com/2013/08/angularjs-and-coffeescript-tutorials.html), [Dart](https://github.com/angular/angular.dart.tutorial/wiki)
* **Realtime: **[Socket.io](http://www.creativebloq.com/javascript/angularjs-collaboration-board-socketio-2132885), [OmniBinder](https://github.com/jeffbcross/omnibinder)
+1 -1
View File
@@ -76,7 +76,7 @@ that you break your application to multiple modules like this:
initialization code.
We've also
[written a document](http://blog.angularjs.org/2014/02/an-angularjs-style-guide-and-best.html)
[written a document](http://angularjs.blogspot.com/2014/02/an-angularjs-style-guide-and-best.html)
on how we organize large apps at Google.
The above is a suggestion. Tailor it to your needs.
+1 -1
View File
@@ -313,7 +313,7 @@ to various URLs and verify that the correct view was rendered.
it('should redirect index.html to index.html#/phones', function() {
browser.get('app/index.html');
browser.getLocationAbsUrl().then(function(url) {
expect(url.split('#')[1]).toBe('/phones');
expect(url).toEqual('/phones');
});
});
+1 -1
View File
@@ -173,7 +173,7 @@ function request(method, url, options, response) {
res.on('error', function (e) { console.log(e); });
break;
case 401:
console.log('Eror: Login credentials expired! Please login.');
console.log('Error: Login credentials expired! Please login.');
break;
default:
data = [];
+8
View File
@@ -0,0 +1,8 @@
#!/bin/bash
set -e -o pipefail
echo "Shutting down Browserstack tunnel"
echo "TODO: implement me"
exit 1
+1 -1
View File
@@ -116,7 +116,7 @@ module.exports = {
.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(/\r?\n/g, '\\n');
js = "!window.angular.$$csp() && window.angular.element(document).find('head').prepend('<style type=\"text/css\">" + css + "</style>');";
js = "!window.angular.$$csp() && window.angular.element(document.head).prepend('<style type=\"text/css\">" + css + "</style>');";
state.js.push(js);
return state;
+58
View File
@@ -0,0 +1,58 @@
'use strict';
var path = require('path');
var fs = require('fs');
var glob = require("glob");
var _ = require('lodash');
var files = require('../../angularFiles').files;
module.exports = function(grunt) {
grunt.registerTask('validate-angular-files', function() {
var combinedFiles = _.clone(files.angularModules);
combinedFiles.ng = files.angularSrc;
combinedFiles.angularLoader = files.angularLoader;
var errorsDetected = false;
var directories = [];
var detectedFiles = {};
for (var section in combinedFiles) {
var sectionFiles = combinedFiles[section];
if (section != 'angularLoader') {
directories.push('src/' + section);
}
grunt.log.debug('Validating ' + sectionFiles.length + ' files from the "' + section + '" module.');
sectionFiles.forEach(function(file) {
detectedFiles[file] = true;
if (!fs.existsSync(file)) {
grunt.log.error(file + ' does not exist in the local file structure.');
errorsDetected = true;
}
});
}
directories.forEach(function(directory) {
glob.sync(directory + '/**/*').forEach(function(filePath) {
if (!fs.lstatSync(filePath).isDirectory()) {
var fileName = path.basename(filePath);
var isHiddenFile = fileName[0] == '.';
if (!isHiddenFile && !detectedFiles[filePath]) {
grunt.log.error(filePath + ' exists in the local file structure but isn\'t used by any module.');
errorsDetected = true;
}
}
});
});
if (errorsDetected) {
throw new Error('Not all files were properly detected in the local file structure.');
} else {
grunt.log.ok('All files were detected successfully!');
}
});
};
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
set -e -o pipefail
echo "Shutting down Sauce Connect tunnel"
killall sc
while [[ -n `ps -ef | grep "sauce-connect-" | grep -v "grep"` ]]; do
printf "."
sleep .5
done
echo ""
echo "Sauce Connect tunnel has been shut down"
+240 -74
View File
@@ -207,7 +207,7 @@
"dependencies": {
"traceur": {
"version": "0.0.33",
"resolved": "git+https://github.com/vojtajina/traceur-compiler#d90b1e34c799bf61cd1aafdc33db0a554fa9e617",
"resolved": "git+https://github.com/vojtajina/traceur-compiler.git#d90b1e34c799bf61cd1aafdc33db0a554fa9e617",
"dependencies": {
"commander": {
"version": "2.7.1",
@@ -2580,14 +2580,6 @@
},
"async-each": {
"version": "0.1.6"
},
"fsevents": {
"version": "0.3.6",
"dependencies": {
"nan": {
"version": "1.8.4"
}
}
}
}
}
@@ -2740,6 +2732,49 @@
}
}
},
"glob": {
"version": "6.0.1",
"dependencies": {
"inflight": {
"version": "1.0.4",
"dependencies": {
"wrappy": {
"version": "1.0.1"
}
}
},
"inherits": {
"version": "2.0.1"
},
"minimatch": {
"version": "3.0.0",
"dependencies": {
"brace-expansion": {
"version": "1.1.2",
"dependencies": {
"balanced-match": {
"version": "0.3.0"
},
"concat-map": {
"version": "0.0.1"
}
}
}
}
},
"once": {
"version": "1.3.3",
"dependencies": {
"wrappy": {
"version": "1.0.1"
}
}
},
"path-is-absolute": {
"version": "1.0.0"
}
}
},
"grunt": {
"version": "0.4.5",
"dependencies": {
@@ -5470,14 +5505,6 @@
},
"async-each": {
"version": "0.1.6"
},
"fsevents": {
"version": "0.3.5",
"dependencies": {
"nan": {
"version": "1.5.3"
}
}
}
}
},
@@ -6505,37 +6532,45 @@
}
},
"protractor": {
"version": "2.0.0",
"version": "2.5.1",
"dependencies": {
"request": {
"version": "2.36.0",
"version": "2.57.0",
"dependencies": {
"qs": {
"version": "0.6.6"
},
"json-stringify-safe": {
"version": "5.0.0"
},
"mime": {
"version": "1.2.11"
},
"forever-agent": {
"version": "0.5.2"
},
"node-uuid": {
"version": "1.4.3"
},
"tough-cookie": {
"version": "0.12.1",
"bl": {
"version": "0.9.4",
"dependencies": {
"punycode": {
"version": "1.3.2"
"readable-stream": {
"version": "1.0.33",
"dependencies": {
"core-util-is": {
"version": "1.0.2"
},
"isarray": {
"version": "0.0.1"
},
"string_decoder": {
"version": "0.10.31"
},
"inherits": {
"version": "2.0.1"
}
}
}
}
},
"caseless": {
"version": "0.10.0"
},
"forever-agent": {
"version": "0.6.1"
},
"form-data": {
"version": "0.1.4",
"version": "0.2.0",
"dependencies": {
"async": {
"version": "0.9.2"
},
"combined-stream": {
"version": "0.0.7",
"dependencies": {
@@ -6543,17 +6578,34 @@
"version": "0.0.5"
}
}
},
"async": {
"version": "0.9.0"
}
}
},
"json-stringify-safe": {
"version": "5.0.1"
},
"mime-types": {
"version": "2.0.14",
"dependencies": {
"mime-db": {
"version": "1.12.0"
}
}
},
"node-uuid": {
"version": "1.4.7"
},
"qs": {
"version": "3.1.0"
},
"tunnel-agent": {
"version": "0.4.0"
"version": "0.4.2"
},
"tough-cookie": {
"version": "2.2.1"
},
"http-signature": {
"version": "0.10.1",
"version": "0.11.0",
"dependencies": {
"assert-plus": {
"version": "0.1.5"
@@ -6567,38 +6619,120 @@
}
},
"oauth-sign": {
"version": "0.3.0"
"version": "0.8.0"
},
"hawk": {
"version": "1.0.0",
"version": "2.3.1",
"dependencies": {
"hoek": {
"version": "0.9.1"
"version": "2.16.3"
},
"boom": {
"version": "0.4.2"
"version": "2.10.1"
},
"cryptiles": {
"version": "0.2.2"
"version": "2.0.5"
},
"sntp": {
"version": "0.2.4"
"version": "1.0.9"
}
}
},
"aws-sign2": {
"version": "0.5.0"
},
"stringstream": {
"version": "0.0.5"
},
"combined-stream": {
"version": "1.0.5",
"dependencies": {
"delayed-stream": {
"version": "1.0.0"
}
}
},
"isstream": {
"version": "0.1.2"
},
"har-validator": {
"version": "1.8.0",
"dependencies": {
"bluebird": {
"version": "2.10.2"
},
"chalk": {
"version": "1.1.1",
"dependencies": {
"ansi-styles": {
"version": "2.1.0"
},
"escape-string-regexp": {
"version": "1.0.3"
},
"has-ansi": {
"version": "2.0.0",
"dependencies": {
"ansi-regex": {
"version": "2.0.0"
}
}
},
"strip-ansi": {
"version": "3.0.0",
"dependencies": {
"ansi-regex": {
"version": "2.0.0"
}
}
},
"supports-color": {
"version": "2.0.0"
}
}
},
"commander": {
"version": "2.9.0",
"dependencies": {
"graceful-readlink": {
"version": "1.0.1"
}
}
},
"is-my-json-valid": {
"version": "2.12.3",
"dependencies": {
"generate-function": {
"version": "2.0.0"
},
"generate-object-property": {
"version": "1.2.0",
"dependencies": {
"is-property": {
"version": "1.0.2"
}
}
},
"jsonpointer": {
"version": "2.0.0"
},
"xtend": {
"version": "4.0.1"
}
}
}
}
}
}
},
"selenium-webdriver": {
"version": "2.45.1",
"version": "2.47.0",
"dependencies": {
"rimraf": {
"version": "2.3.2",
"version": "2.4.4",
"dependencies": {
"glob": {
"version": "4.5.3",
"version": "5.0.15",
"dependencies": {
"inflight": {
"version": "1.0.4",
@@ -6612,13 +6746,13 @@
"version": "2.0.1"
},
"minimatch": {
"version": "2.0.4",
"version": "3.0.0",
"dependencies": {
"brace-expansion": {
"version": "1.1.0",
"version": "1.1.2",
"dependencies": {
"balanced-match": {
"version": "0.2.0"
"version": "0.3.0"
},
"concat-map": {
"version": "0.0.1"
@@ -6628,12 +6762,15 @@
}
},
"once": {
"version": "1.3.1",
"version": "1.3.3",
"dependencies": {
"wrappy": {
"version": "1.0.1"
}
}
},
"path-is-absolute": {
"version": "1.0.0"
}
}
}
@@ -6643,33 +6780,33 @@
"version": "0.0.24"
},
"ws": {
"version": "0.7.1",
"version": "0.8.1",
"dependencies": {
"options": {
"version": "0.0.6"
},
"ultron": {
"version": "1.0.1"
"version": "1.0.2"
},
"bufferutil": {
"version": "1.0.1",
"version": "1.2.1",
"dependencies": {
"bindings": {
"version": "1.2.1"
},
"nan": {
"version": "1.6.2"
"version": "2.1.0"
}
}
},
"utf-8-validate": {
"version": "1.0.1",
"version": "1.2.1",
"dependencies": {
"bindings": {
"version": "1.2.1"
},
"nan": {
"version": "1.6.2"
"version": "2.1.0"
}
}
}
@@ -6682,10 +6819,10 @@
"version": "0.6.1"
},
"xmlbuilder": {
"version": "2.6.2",
"version": "4.2.0",
"dependencies": {
"lodash": {
"version": "3.5.0"
"version": "3.10.1"
}
}
}
@@ -6700,18 +6837,47 @@
"version": "1.1.0"
},
"jasminewd2": {
"version": "0.0.3"
"version": "0.0.6"
},
"jasmine": {
"version": "2.1.1",
"version": "2.3.2",
"dependencies": {
"exit": {
"version": "0.1.2"
},
"jasmine-core": {
"version": "2.1.3"
"version": "2.3.4"
}
}
},
"saucelabs": {
"version": "0.1.1"
"version": "1.0.1",
"dependencies": {
"https-proxy-agent": {
"version": "1.0.0",
"dependencies": {
"agent-base": {
"version": "2.0.1",
"dependencies": {
"semver": {
"version": "5.0.3"
}
}
},
"debug": {
"version": "2.2.0",
"dependencies": {
"ms": {
"version": "0.7.1"
}
}
},
"extend": {
"version": "3.0.0"
}
}
}
}
},
"glob": {
"version": "3.2.11",
@@ -6723,10 +6889,10 @@
"version": "0.3.0",
"dependencies": {
"lru-cache": {
"version": "2.5.0"
"version": "2.7.3"
},
"sigmund": {
"version": "1.0.0"
"version": "1.0.1"
}
}
}
@@ -6739,7 +6905,7 @@
"version": "0.6.1",
"dependencies": {
"wordwrap": {
"version": "0.0.2"
"version": "0.0.3"
},
"minimist": {
"version": "0.0.10"
@@ -6756,14 +6922,14 @@
"version": "0.1.32",
"dependencies": {
"amdefine": {
"version": "0.1.0"
"version": "1.0.0"
}
}
}
}
},
"html-entities": {
"version": "1.1.2"
"version": "1.1.3"
},
"accessibility-developer-tools": {
"version": "2.6.0"
+544 -290
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -1,6 +1,7 @@
{
"name": "angularjs",
"branchVersion": "1.3.*",
"distTag": "previous_1_3",
"repository": {
"type": "git",
"url": "https://github.com/angular/angular.js.git"
@@ -10,6 +11,10 @@
"npm": "~2.5"
},
"engineStrict": true,
"scripts": {
"preinstall": "node scripts/npm/check-node-modules.js --purge",
"postinstall": "node scripts/npm/copy-npm-shrinkwrap.js"
},
"devDependencies": {
"angular-benchpress": "0.x.x",
"benchmark": "1.x.x",
@@ -20,6 +25,7 @@
"dgeni": "^0.4.0",
"dgeni-packages": "^0.10.0",
"event-stream": "~3.1.0",
"glob": "^6.0.1",
"grunt": "~0.4.2",
"grunt-bump": "~0.0.13",
"grunt-contrib-clean": "~0.6.0",
+4 -13
View File
@@ -29,6 +29,8 @@ function init {
angular-touch
angular-messages
)
# get the npm dist-tag from a custom property (distTag) in package.json
DIST_TAG=$(readJsonProp "package.json" "distTag")
}
@@ -110,19 +112,8 @@ function publish {
# don't publish every build to npm
if [ "${NEW_VERSION/+sha}" = "$NEW_VERSION" ] ; then
if [ "${NEW_VERSION/-}" = "$NEW_VERSION" ] ; then
if [[ $NEW_VERSION =~ ^1\.2\.[0-9]+$ ]] ; then
# publish 1.2.x releases with the appropriate tag
# this ensures that `npm install` by default will not grab `1.2.x` releases
npm publish --tag=old
else
# publish releases as "latest"
npm publish
fi
else
# publish prerelease builds with the beta tag
npm publish --tag=beta
fi
echo "-- Publishing to npm as $DIST_TAG"
npm publish --tag=$DIST_TAG
fi
cd $SCRIPT_DIR
@@ -7,6 +7,8 @@ echo "#################################"
# Enable tracing and exit on first failure
set -xe
scripts/jenkins/set-node-version.sh
# This is the default set of browsers to use on the CI server unless overridden via env variable
if [[ -z "$BROWSERS" ]]
then
@@ -19,6 +21,7 @@ rm -f angular.js.size
# BUILD #
npm install -g grunt-cli
npm install --color false
grunt ci-checks package --no-color
+2 -11
View File
@@ -4,9 +4,7 @@ echo "#################################"
echo "#### Update master ##############"
echo "#################################"
ARG_DEFS=(
"[--no-test=(true|false)]"
)
ARG_DEFS=()
function init {
if [[ ! $VERBOSE ]]; then
@@ -17,14 +15,7 @@ function init {
function build {
cd ../..
if [[ $NO_TEST == "true" ]]; then
npm install --color false
grunt ci-checks package --no-color
else
./jenkins_build.sh
fi
scripts/jenkins/build.sh
cd $SCRIPT_DIR
}
+2
View File
@@ -35,8 +35,10 @@ function init {
}
function build {
./set-node-version.sh
cd ../..
npm install -g grunt-cli
npm install --color false
grunt ci-checks package --no-color
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash
# Install nvm for this shell
source ~/.nvm/nvm.sh
# Use node.js at 4.2.x
nvm install 4.2
+74
View File
@@ -0,0 +1,74 @@
// Implementation based on:
// https://github.com/angular/angular/blob/3b9c08676a4c921bbfa847802e08566fb601ba7a/tools/npm/check-node-modules.js
'use strict';
// Imports
var fs = require('fs');
var path = require('path');
// Constants
var PROJECT_ROOT = path.join(__dirname, '../../');
var NODE_MODULES_DIR = 'node_modules';
var NPM_SHRINKWRAP_FILE = 'npm-shrinkwrap.json';
var NPM_SHRINKWRAP_CACHED_FILE = NODE_MODULES_DIR + '/npm-shrinkwrap.cached.json';
// Run
_main();
// Functions - Definitions
function _main() {
var purgeIfStale = process.argv.indexOf('--purge') !== -1;
process.chdir(PROJECT_ROOT);
checkNodeModules(purgeIfStale);
}
function checkNodeModules(purgeIfStale) {
var nodeModulesOk = compareMarkerFiles(NPM_SHRINKWRAP_FILE, NPM_SHRINKWRAP_CACHED_FILE);
if (nodeModulesOk) {
console.log(':-) npm dependencies are looking good!');
} else if (purgeIfStale) {
console.log(':-( npm dependencies are stale or in an unknown state!');
console.log(' Purging \'' + NODE_MODULES_DIR + '\'...');
deleteDirSync(NODE_MODULES_DIR);
} else {
var separator = new Array(81).join('!');
console.warn(separator);
console.warn(':-( npm dependencies are stale or in an unknown state!');
console.warn('You can rebuild the dependencies by running `npm install`.');
console.warn(separator);
}
return nodeModulesOk;
}
function compareMarkerFiles(markerFilePath, cachedMarkerFilePath) {
if (!fs.existsSync(cachedMarkerFilePath)) return false;
var opts = {encoding: 'utf-8'};
var markerContent = fs.readFileSync(markerFilePath, opts);
var cachedMarkerContent = fs.readFileSync(cachedMarkerFilePath, opts);
return markerContent === cachedMarkerContent;
}
// Custom implementation of `rm -rf` that works consistently across OSes
function deleteDirSync(path) {
if (fs.existsSync(path)) {
fs.readdirSync(path).forEach(deleteDirOrFileSync);
fs.rmdirSync(path);
}
// Helpers
function deleteDirOrFileSync(subpath) {
var curPath = path + '/' + subpath;
if (fs.lstatSync(curPath).isDirectory()) {
deleteDirSync(curPath);
} else {
fs.unlinkSync(curPath);
}
}
}
+60
View File
@@ -0,0 +1,60 @@
'use strict';
// Imports
var fs = require('fs');
var path = require('path');
// Constants
var PROJECT_ROOT = path.join(__dirname, '../../');
var NODE_MODULES_DIR = 'node_modules';
var NPM_SHRINKWRAP_FILE = 'npm-shrinkwrap.json';
var NPM_SHRINKWRAP_CACHED_FILE = NODE_MODULES_DIR + '/npm-shrinkwrap.cached.json';
// Run
_main();
// Functions - Definitions
function _main() {
process.chdir(PROJECT_ROOT);
copyFile(NPM_SHRINKWRAP_FILE, NPM_SHRINKWRAP_CACHED_FILE, onCopied);
}
// Implementation based on:
// https://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js#answer-21995878
function copyFile(srcPath, dstPath, callback) {
var callbackCalled = false;
if (!fs.existsSync(srcPath)) {
done(new Error('Missing source file: ' + srcPath));
return;
}
var rs = fs.createReadStream(srcPath);
rs.on('error', done);
var ws = fs.createWriteStream(dstPath);
ws.on('error', done);
ws.on('finish', done);
rs.pipe(ws);
// Helpers
function done(err) {
if (callback && !callbackCalled) {
callbackCalled = true;
callback(err);
}
}
}
function onCopied(err) {
if (err) {
var separator = new Array(81).join('!');
console.error(separator);
console.error(
'Failed to copy `' + NPM_SHRINKWRAP_FILE + '` to `' + NPM_SHRINKWRAP_CACHED_FILE + '`:');
console.error(err);
console.error(separator);
}
}
-16
View File
@@ -1,16 +0,0 @@
#!/bin/bash
set -e
SHRINKWRAP_FILE=npm-shrinkwrap.json
SHRINKWRAP_CACHED_FILE=node_modules/npm-shrinkwrap.cached.json
if diff -q $SHRINKWRAP_FILE $SHRINKWRAP_CACHED_FILE; then
echo 'No shrinkwrap changes detected. npm install will be skipped...';
else
echo 'Blowing away node_modules and reinstalling npm dependencies...'
rm -rf node_modules
npm install
cp $SHRINKWRAP_FILE $SHRINKWRAP_CACHED_FILE
echo 'npm install successful!'
fi
+18
View File
@@ -0,0 +1,18 @@
#!/bin/bash
set -e
mkdir -p $LOGS_DIR
if [ $JOB != "ci-checks" ]; then
echo "start_browser_provider"
./scripts/travis/start_browser_provider.sh
fi
npm install -g grunt-cli
if [ $JOB != "ci-checks" ]; then
grunt package
echo "wait_for_browser_provider"
./scripts/travis/wait_for_browser_provider.sh
fi
+4 -3
View File
@@ -5,7 +5,9 @@ set -e
export BROWSER_STACK_ACCESS_KEY=`echo $BROWSER_STACK_ACCESS_KEY | rev`
export SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
if [ $JOB = "unit" ]; then
if [ $JOB = "ci-checks" ]; then
grunt ci-checks
elif [ $JOB = "unit" ]; then
if [ "$BROWSER_PROVIDER" == "browserstack" ]; then
BROWSERS="BS_Chrome,BS_Safari,BS_Firefox,BS_IE_9,BS_IE_10,BS_IE_11,BS_iOS"
else
@@ -14,7 +16,6 @@ if [ $JOB = "unit" ]; then
grunt test:promises-aplus
grunt test:unit --browsers $BROWSERS --reporters dots
grunt ci-checks
grunt tests:docs --browsers $BROWSERS --reporters dots
elif [ $JOB = "docs-e2e" ]; then
grunt test:travis-protractor --specs "docs/app/e2e/**/*.scenario.js"
@@ -31,5 +32,5 @@ elif [ $JOB = "e2e" ]; then
export TARGET_SPECS="test/e2e/tests/**/*.js,$TARGET_SPECS"
grunt test:travis-protractor --specs "$TARGET_SPECS"
else
echo "Unknown job type. Please set JOB=unit or JOB=e2e-*."
echo "Unknown job type. Please set JOB=ci-checks, JOB=unit or JOB=e2e-*."
fi
+4
View File
@@ -0,0 +1,4 @@
#!/bin/bash
# Has to be run from project root directory.
./lib/${BROWSER_PROVIDER}/teardown_tunnel.sh
+111 -59
View File
@@ -36,6 +36,7 @@
isUndefined: true,
isDefined: true,
isObject: true,
isBlankObject: true,
isString: true,
isNumber: true,
isDate: true,
@@ -172,6 +173,7 @@ var
splice = [].splice,
push = [].push,
toString = Object.prototype.toString,
getPrototypeOf = Object.getPrototypeOf,
ngMinErr = minErr('ng'),
/** @name angular */
@@ -461,6 +463,16 @@ function isObject(value) {
}
/**
* Determine if a value is an object with a null prototype
*
* @returns {boolean} True if `value` is an `Object` with a null prototype
*/
function isBlankObject(value) {
return value !== null && typeof value === 'object' && !getPrototypeOf(value);
}
/**
* @ngdoc function
* @name angular.isString
@@ -597,6 +609,12 @@ function isPromiseLike(obj) {
}
var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
function isTypedArray(value) {
return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
}
var trim = function(value) {
return isString(value) ? value.trim() : value;
};
@@ -713,77 +731,111 @@ function arrayRemove(array, value) {
</file>
</example>
*/
function copy(source, destination, stackSource, stackDest) {
if (isWindow(source) || isScope(source)) {
throw ngMinErr('cpws',
"Can't copy! Making copies of Window or Scope instances is not supported.");
function copy(source, destination) {
var stackSource = [];
var stackDest = [];
if (destination) {
if (isTypedArray(destination)) {
throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
}
if (source === destination) {
throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
}
// Empty the destination object
if (isArray(destination)) {
destination.length = 0;
} else {
forEach(destination, function(value, key) {
if (key !== '$$hashKey') {
delete destination[key];
}
});
}
stackSource.push(source);
stackDest.push(destination);
return copyRecurse(source, destination);
}
if (!destination) {
destination = source;
if (source) {
if (isArray(source)) {
destination = copy(source, [], stackSource, stackDest);
} else if (isDate(source)) {
destination = new Date(source.getTime());
} else if (isRegExp(source)) {
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
destination.lastIndex = source.lastIndex;
} else if (isObject(source)) {
var emptyObject = Object.create(Object.getPrototypeOf(source));
destination = copy(source, emptyObject, stackSource, stackDest);
}
}
} else {
if (source === destination) throw ngMinErr('cpi',
"Can't copy! Source and destination are identical.");
return copyElement(source);
stackSource = stackSource || [];
stackDest = stackDest || [];
if (isObject(source)) {
var index = stackSource.indexOf(source);
if (index !== -1) return stackDest[index];
stackSource.push(source);
stackDest.push(destination);
}
var result;
function copyRecurse(source, destination) {
var h = destination.$$hashKey;
var result, key;
if (isArray(source)) {
destination.length = 0;
for (var i = 0; i < source.length; i++) {
result = copy(source[i], null, stackSource, stackDest);
if (isObject(source[i])) {
stackSource.push(source[i]);
stackDest.push(result);
for (var i = 0, ii = source.length; i < ii; i++) {
destination.push(copyElement(source[i]));
}
} else if (isBlankObject(source)) {
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
for (key in source) {
destination[key] = copyElement(source[key]);
}
} else if (source && typeof source.hasOwnProperty === 'function') {
// Slow path, which must rely on hasOwnProperty
for (key in source) {
if (source.hasOwnProperty(key)) {
destination[key] = copyElement(source[key]);
}
destination.push(result);
}
} else {
var h = destination.$$hashKey;
if (isArray(destination)) {
destination.length = 0;
} else {
forEach(destination, function(value, key) {
delete destination[key];
});
}
for (var key in source) {
if (source.hasOwnProperty(key)) {
result = copy(source[key], null, stackSource, stackDest);
if (isObject(source[key])) {
stackSource.push(source[key]);
stackDest.push(result);
}
destination[key] = result;
// Slowest path --- hasOwnProperty can't be called as a method
for (key in source) {
if (hasOwnProperty.call(source, key)) {
destination[key] = copyElement(source[key]);
}
}
setHashKey(destination,h);
}
setHashKey(destination, h);
return destination;
}
function copyElement(source) {
// Simple values
if (!isObject(source)) {
return source;
}
// Already copied values
var index = stackSource.indexOf(source);
if (index !== -1) {
return stackDest[index];
}
if (isWindow(source) || isScope(source)) {
throw ngMinErr('cpws',
"Can't copy! Making copies of Window or Scope instances is not supported.");
}
var needsRecurse = false;
var destination;
if (isArray(source)) {
destination = [];
needsRecurse = true;
} else if (isTypedArray(source)) {
destination = new source.constructor(source);
} else if (isDate(source)) {
destination = new Date(source.getTime());
} else if (isRegExp(source)) {
destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
destination.lastIndex = source.lastIndex;
} else if (isFunction(source.cloneNode)) {
destination = source.cloneNode(true);
} else {
destination = Object.create(getPrototypeOf(source));
needsRecurse = true;
}
stackSource.push(source);
stackDest.push(destination);
return needsRecurse
? copyRecurse(source, destination)
: destination;
}
return destination;
}
/**
+1 -10
View File
@@ -429,7 +429,7 @@
*
* ### Transclusion
*
* Transclusion is the process of extracting a collection of DOM element from one part of the DOM and
* Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
* copying them to another part of the DOM, while maintaining their connection to the original AngularJS
* scope from where they were taken.
*
@@ -757,14 +757,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return bindings;
}
function assertValidDirectiveName(name) {
var letter = name.charAt(0);
if (!letter || letter !== lowercase(letter)) {
throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
}
return name;
}
/**
* @ngdoc method
* @name $compileProvider#directive
@@ -783,7 +775,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
this.directive = function registerDirective(name, directiveFactory) {
assertNotHasOwnProperty(name, 'directive');
if (isString(name)) {
assertValidDirectiveName(name);
assertArg(directiveFactory, 'directiveFactory');
if (!hasDirectives.hasOwnProperty(name)) {
hasDirectives[name] = [];
+3 -7
View File
@@ -318,13 +318,9 @@ function FormController(element, attrs, $scope, $animate, $interpolate) {
*
* In Angular, forms can be nested. This means that the outer form is valid when all of the child
* forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
* Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
* `<form>` but can be nested. This allows you to have nested forms, which is very useful when
* using Angular validation directives in forms that are dynamically generated using the
* {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
* attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
* `ngForm` directive and nest these in an outer `form` element.
*
* Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
* `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
* of controls needs to be determined.
*
* # CSS classes
* - `ng-valid` is set if the form is valid.
+13 -10
View File
@@ -6,7 +6,7 @@
DIRTY_CLASS: false,
UNTOUCHED_CLASS: false,
TOUCHED_CLASS: false,
$ngModelMinErr: false,
ngModelMinErr: false,
*/
// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
@@ -1170,7 +1170,7 @@ function createDateInputType(type, regexp, parseDate, format) {
ctrl.$formatters.push(function(value) {
if (value && !isDate(value)) {
throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
}
if (isValidDate(value)) {
previousDate = value;
@@ -1247,7 +1247,7 @@ function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
ctrl.$formatters.push(function(value) {
if (!ctrl.$isEmpty(value)) {
if (!isNumber(value)) {
throw $ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
}
value = value.toString();
}
@@ -1340,7 +1340,7 @@ function parseConstantExpr($parse, context, name, expression, fallback) {
if (isDefined(expression)) {
parseFn = $parse(expression);
if (!parseFn.constant) {
throw minErr('ngModel')('constexpr', 'Expected constant expression for `{0}`, but saw ' +
throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
'`{1}`.', name, expression);
}
return parseFn(context);
@@ -1535,13 +1535,16 @@ var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
return {
restrict: 'E',
require: ['?ngModel'],
link: {
pre: function(scope, element, attr, ctrls) {
if (ctrls[0]) {
(inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
$browser, $filter, $parse);
compile: function(tElement, tAttr) {
if (lowercase(tAttr.type) === 'hidden') tAttr.$set('autocomplete', 'off');
return {
pre: function(scope, element, attr, ctrls) {
if (ctrls[0]) {
(inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
$browser, $filter, $parse);
}
}
}
};
}
};
}];
+3 -4
View File
@@ -16,8 +16,7 @@ var VALID_CLASS = 'ng-valid',
TOUCHED_CLASS = 'ng-touched',
PENDING_CLASS = 'ng-pending';
var $ngModelMinErr = new minErr('ngModel');
var ngModelMinErr = minErr('ngModel');
/**
* @ngdoc type
@@ -268,7 +267,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
}
};
} else if (!parsedNgModel.assign) {
throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
$attr.ngModel, startingTag($element));
}
};
@@ -597,7 +596,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
forEach(ctrl.$asyncValidators, function(validator, name) {
var promise = validator(modelValue, viewValue);
if (!isPromiseLike(promise)) {
throw $ngModelMinErr("$asyncValidators",
throw ngModelMinErr('nopromise',
"Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
}
setValidity(name, undefined);
+3 -2
View File
@@ -43,8 +43,9 @@ var patternDirective = function() {
ctrl.$validate();
});
ctrl.$validators.pattern = function(value) {
return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value);
ctrl.$validators.pattern = function(modelValue, viewValue) {
// HTML5 pattern constraint validates the input value, so we validate the viewValue
return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
};
}
};
+2 -2
View File
@@ -1110,8 +1110,8 @@ function $HttpProvider() {
* Resolves the raw $http promise.
*/
function resolvePromise(response, status, headers, statusText) {
// normalize internal statuses to 0
status = Math.max(status, 0);
//status: HTTP response status code, 0, -1 (aborted by timeout / promise)
status = status >= -1 ? status : 0;
(isSuccess(status) ? deferred.resolve : deferred.reject)({
data: response,
+1 -1
View File
@@ -58,7 +58,7 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
xhr.onload = function requestLoaded() {
var statusText = xhr.statusText || '';
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
// responseText is the old-school way of retrieving response (supported by IE9)
// response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
var response = ('response' in xhr) ? xhr.response : xhr.responseText;
+17 -9
View File
@@ -89,12 +89,12 @@ function serverBase(url) {
*
* @constructor
* @param {string} appBase application base URL
* @param {string} appBaseNoFile application base URL stripped of any filename
* @param {string} basePrefix url path prefix
*/
function LocationHtml5Url(appBase, basePrefix) {
function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
this.$$html5 = true;
basePrefix = basePrefix || '';
var appBaseNoFile = stripFile(appBase);
parseAbsoluteUrl(appBase, this);
@@ -168,10 +168,10 @@ function LocationHtml5Url(appBase, basePrefix) {
*
* @constructor
* @param {string} appBase application base URL
* @param {string} appBaseNoFile application base URL stripped of any filename
* @param {string} hashPrefix hashbang prefix
*/
function LocationHashbangUrl(appBase, hashPrefix) {
var appBaseNoFile = stripFile(appBase);
function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
parseAbsoluteUrl(appBase, this);
@@ -280,14 +280,13 @@ function LocationHashbangUrl(appBase, hashPrefix) {
*
* @constructor
* @param {string} appBase application base URL
* @param {string} appBaseNoFile application base URL stripped of any filename
* @param {string} hashPrefix hashbang prefix
*/
function LocationHashbangInHtml5Url(appBase, hashPrefix) {
function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
this.$$html5 = true;
LocationHashbangUrl.apply(this, arguments);
var appBaseNoFile = stripFile(appBase);
this.$$parseLinkUrl = function(url, relHref) {
if (relHref && relHref[0] === '#') {
// special case for links to hash fragments:
@@ -317,7 +316,7 @@ function LocationHashbangInHtml5Url(appBase, hashPrefix) {
hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
// include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#'
// include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
this.$$absUrl = appBase + hashPrefix + this.$$url;
};
@@ -823,7 +822,9 @@ function $LocationProvider() {
appBase = stripHash(initialUrl);
LocationMode = LocationHashbangUrl;
}
$location = new LocationMode(appBase, '#' + hashPrefix);
var appBaseNoFile = stripFile(appBase);
$location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
$location.$$parseLinkUrl(initialUrl, initialUrl);
$location.$$state = $browser.state();
@@ -903,6 +904,13 @@ function $LocationProvider() {
// update $location when $browser url changes
$browser.onUrlChange(function(newUrl, newState) {
if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
// If we are navigating outside of the app then force a reload
$window.location.href = newUrl;
return;
}
$rootScope.$evalAsync(function() {
var oldUrl = $location.absUrl();
var oldState = $location.$$state;
+21 -2
View File
@@ -48,6 +48,25 @@ function ensureSafeMemberName(name, fullExpression) {
return name;
}
function getStringValue(name, fullExpression) {
// From the JavaScript docs:
// Property names must be strings. This means that non-string objects cannot be used
// as keys in an object. Any non-string object, including a number, is typecasted
// into a string via the toString method.
//
// So, to ensure that we are checking the same `name` that JavaScript would use,
// we cast it to a string, if possible.
// Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
// this is, this will handle objects that misbehave.
name = name + '';
if (!isString(name)) {
throw $parseMinErr('iseccst',
'Cannot convert object to primitive value! '
+ 'Expression: {0}', fullExpression);
}
return name;
}
function ensureSafeObject(obj, fullExpression) {
// nifty check if obj is Function that is fast and works across iframes and other contexts
if (obj) {
@@ -689,7 +708,7 @@ Parser.prototype = {
return extend(function $parseObjectIndex(self, locals) {
var o = obj(self, locals),
i = indexFn(self, locals),
i = getStringValue(indexFn(self, locals), expression),
v;
ensureSafeMemberName(i, expression);
@@ -698,7 +717,7 @@ Parser.prototype = {
return v;
}, {
assign: function(self, value, locals) {
var key = ensureSafeMemberName(indexFn(self, locals), expression);
var key = ensureSafeMemberName(getStringValue(indexFn(self, locals), expression), expression);
// prevent overwriting of Function.constructor which would break ensureSafeObject check
var o = ensureSafeObject(obj(self, locals), expression);
if (!o) obj.assign(self, o = {}, locals);
+5 -8
View File
@@ -108,12 +108,9 @@ function $RootScopeProvider() {
* A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
* {@link auto.$injector $injector}. Child scopes are created using the
* {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
* compiled HTML template is executed.)
* compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
* an in-depth introduction and usage examples.
*
* Here is a simple scope snippet to show how you can interact with the scope.
* ```html
* <file src="./test/ng/rootScopeSpec.js" tag="docs1" />
* ```
*
* # Inheritance
* A scope can inherit from a parent scope, as in this example:
@@ -274,9 +271,9 @@ function $RootScopeProvider() {
*
*
* If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
* you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
* can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
* change is detected, be prepared for multiple calls to your listener.)
* you can register a `watchExpression` function with no `listener`. (Be prepared for
* multiple calls to your `watchExpression` because it will execute multiple times in a
* single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
*
* After a watcher is registered with the scope, the `listener` fn is called asynchronously
* (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
+4 -4
View File
@@ -545,7 +545,7 @@ function $SceDelegateProvider() {
* characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'. It's a useful wildcard for use
* in a whitelist.
* - `**`: matches zero or more occurrences of *any* character. As such, it's not
* not appropriate to use in for a scheme, domain, etc. as it would match too much. (e.g.
* appropriate for use in a scheme, domain, etc. as it would match too much. (e.g.
* http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
* not have been the intention.) Its usage at the very end of the path is ok. (e.g.
* http://foo.example.com/templates/**).
@@ -553,11 +553,11 @@ function $SceDelegateProvider() {
* - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
* (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
* accidentally introduce a bug when one updates a complex expression (imho, all regexes should
* have good test coverage.). For instance, the use of `.` in the regex is correct only in a
* have good test coverage). For instance, the use of `.` in the regex is correct only in a
* small number of cases. A `.` character in the regex used when matching the scheme or a
* subdomain could be matched against a `:` or literal `.` that was likely not intended. It
* is highly recommended to use the string patterns and only fall back to regular expressions
* if they as a last resort.
* as a last resort.
* - The regular expression must be an instance of RegExp (i.e. not a string.) It is
* matched against the **entire** *normalized / absolute URL* of the resource being tested
* (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
@@ -567,7 +567,7 @@ function $SceDelegateProvider() {
* remember to escape your regular expression (and be aware that you might need more than
* one level of escaping depending on your templating engine and the way you interpolated
* the value.) Do make use of your platform's escaping mechanism as it might be good
* enough before coding your own. e.g. Ruby has
* enough before coding your own. E.g. Ruby has
* [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
* and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
* Javascript lacks a similar built in function for escaping. Take a look at Google
+1 -8
View File
@@ -24,20 +24,13 @@ var originUrl = urlResolve(window.location.href);
*
* Implementation Notes for IE
* ---------------------------
* IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other
* IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
* browsers. However, the parsed components will not be set if the URL assigned did not specify
* them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
* work around that by performing the parsing in a 2nd step by taking a previously normalized
* URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
* properties such as protocol, hostname, port, etc.
*
* IE7 does not normalize the URL when assigned to an anchor node. (Apparently, it does, if one
* uses the inner HTML approach to assign the URL as part of an HTML snippet -
* http://stackoverflow.com/a/472729) However, setting img[src] does normalize the URL.
* Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception.
* Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that
* method and IE < 8 is unsupported.
*
* References:
* http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
* http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
+11 -8
View File
@@ -1194,18 +1194,21 @@ angular.module('ngAnimate', ['ng'])
}
return cache.promise = runAnimationPostDigest(function(done) {
var parentElement = element.parent();
var elementNode = extractElementNode(element);
var parentNode = elementNode.parentNode;
var cache, parentNode, parentElement, elementNode = extractElementNode(element);
if (elementNode) {
cache = element.data(STORAGE_KEY);
element.removeData(STORAGE_KEY);
parentElement = element.parent();
parentNode = elementNode.parentNode;
}
// TODO(matsko): move this code into the animationsDisabled() function once #8092 is fixed
if (!parentNode || parentNode['$$NG_REMOVED'] || elementNode['$$NG_REMOVED']) {
done();
return;
}
var cache = element.data(STORAGE_KEY);
element.removeData(STORAGE_KEY);
var state = element.data(NG_ANIMATE_STATE) || {};
var classes = resolveElementClasses(element, cache, state.active);
return !classes
@@ -1953,7 +1956,7 @@ angular.module('ngAnimate', ['ng'])
function onAnimationProgress(event) {
event.stopPropagation();
var ev = event.originalEvent || event;
var timeStamp = ev.$manualTimeStamp || ev.timeStamp || Date.now();
var timeStamp = ev.$manualTimeStamp || Date.now();
/* Firefox (or possibly just Gecko) likes to not round values up
* when a ms measurement is used for the animation */
@@ -1961,7 +1964,7 @@ angular.module('ngAnimate', ['ng'])
/* $manualTimeStamp is a mocked timeStamp value which is set
* within browserTrigger(). This is only here so that tests can
* mock animations properly. Real events fallback to event.timeStamp,
* mock animations properly. Real events fallback to Date.now(),
* or, if they don't, then a timeStamp is automatically created for them.
* We're checking to see if the timeStamp surpasses the expected delay,
* but we're using elapsedTime instead of the timeStamp on the 2nd
+48 -10
View File
@@ -782,8 +782,8 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
};
});
$provide.decorator('$animate', ['$delegate', '$$asyncCallback', '$timeout', '$browser',
function($delegate, $$asyncCallback, $timeout, $browser) {
$provide.decorator('$animate', ['$delegate', '$$asyncCallback', '$timeout', '$browser', '$rootScope', '$$rAF',
function($delegate, $$asyncCallback, $timeout, $browser, $rootScope, $$rAF) {
var animate = {
queue: [],
cancel: $delegate.cancel,
@@ -803,6 +803,43 @@ angular.mock.animate = angular.module('ngAnimateMock', ['ng'])
fn();
});
reflowQueue = [];
},
flush: function() {
$rootScope.$digest();
var doNextRun, somethingFlushed = false;
do {
doNextRun = false;
if (reflowQueue.length) {
doNextRun = somethingFlushed = true;
this.triggerReflow();
}
if ($$rAF.queue.length) {
doNextRun = somethingFlushed = true;
$$rAF.flush();
}
if ($$asyncCallback.queue.length) {
doNextRun = somethingFlushed = true;
this.triggerCallbackEvents();
}
if (timeoutsRemaining()) {
var oldValue = timeoutsRemaining();
this.triggerCallbackPromise();
var newValue = timeoutsRemaining();
if (newValue < oldValue) {
doNextRun = somethingFlushed = true;
}
}
} while (doNextRun);
if (!somethingFlushed) {
throw new Error('No pending animations ready to be closed or flushed');
}
$rootScope.$digest();
function timeoutsRemaining() {
return $browser.deferredFns.length;
}
}
};
@@ -1752,8 +1789,7 @@ angular.mock.$TimeoutDecorator = ['$delegate', '$browser', function($delegate, $
}];
angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
var queue = [];
var rafFn = function(fn) {
var queue, rafFn = function(fn) {
var index = queue.length;
queue.push(fn);
return function() {
@@ -1761,6 +1797,8 @@ angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
};
};
queue = rafFn.queue = [];
rafFn.supported = $delegate.supported;
rafFn.flush = function() {
@@ -1773,22 +1811,22 @@ angular.mock.$RAFDecorator = ['$delegate', function($delegate) {
queue[i]();
}
queue = [];
queue.length = 0;
};
return rafFn;
}];
angular.mock.$AsyncCallbackDecorator = ['$delegate', function($delegate) {
var callbacks = [];
var addFn = function(fn) {
callbacks.push(fn);
var queue, addFn = function(fn) {
queue.push(fn);
};
queue = addFn.queue = [];
addFn.flush = function() {
angular.forEach(callbacks, function(fn) {
angular.forEach(queue, function(fn) {
fn();
});
callbacks = [];
queue.length = 0;
};
return addFn;
}];
+3 -1
View File
@@ -407,7 +407,9 @@ function $RouteProvider() {
* @name $route#$routeChangeSuccess
* @eventType broadcast on root scope
* @description
* Broadcasted after a route dependencies are resolved.
* Broadcasted after a route change has happened successfully.
* The `resolve` dependencies are now available in the `current.locals` property.
*
* {@link ngRoute.directive:ngView ngView} listens for the directive
* to instantiate the controller and render the view.
*
+2 -1
View File
@@ -165,6 +165,7 @@
"spyOnlyCallsWithArgs": false,
"createMockStyleSheet": false,
"browserTrigger": false,
"jqLiteCacheSize": false
"jqLiteCacheSize": false,
"browserSupportsCssAnimations": false
}
}
+283 -2
View File
@@ -78,6 +78,186 @@ describe('angular', function() {
expect(copy(objWithRegExp.re) === objWithRegExp.re).toBeFalsy();
});
it("should copy a Uint8Array with no destination", function() {
if (typeof Uint8Array !== 'undefined') {
var src = new Uint8Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Uint8Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Uint8ClampedArray with no destination", function() {
if (typeof Uint8ClampedArray !== 'undefined') {
var src = new Uint8ClampedArray(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Uint8ClampedArray).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Uint16Array with no destination", function() {
if (typeof Uint16Array !== 'undefined') {
var src = new Uint16Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Uint16Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Uint32Array with no destination", function() {
if (typeof Uint32Array !== 'undefined') {
var src = new Uint32Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Uint32Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Int8Array with no destination", function() {
if (typeof Int8Array !== 'undefined') {
var src = new Int8Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Int8Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Int16Array with no destination", function() {
if (typeof Int16Array !== 'undefined') {
var src = new Int16Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Int16Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Int32Array with no destination", function() {
if (typeof Int32Array !== 'undefined') {
var src = new Int32Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Int32Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Float32Array with no destination", function() {
if (typeof Float32Array !== 'undefined') {
var src = new Float32Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Float32Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should copy a Float64Array with no destination", function() {
if (typeof Float64Array !== 'undefined') {
var src = new Float64Array(2);
src[1] = 1;
var dst = copy(src);
expect(copy(src) instanceof Float64Array).toBeTruthy();
expect(dst).toEqual(src);
expect(dst).not.toBe(src);
}
});
it("should throw an exception if a Uint8Array is the destination", function() {
if (typeof Uint8Array !== 'undefined') {
var src = new Uint8Array();
var dst = new Uint8Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Uint8ClampedArray is the destination", function() {
if (typeof Uint8ClampedArray !== 'undefined') {
var src = new Uint8ClampedArray();
var dst = new Uint8ClampedArray(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Uint16Array is the destination", function() {
if (typeof Uint16Array !== 'undefined') {
var src = new Uint16Array();
var dst = new Uint16Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Uint32Array is the destination", function() {
if (typeof Uint32Array !== 'undefined') {
var src = new Uint32Array();
var dst = new Uint32Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Int8Array is the destination", function() {
if (typeof Int8Array !== 'undefined') {
var src = new Int8Array();
var dst = new Int8Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Int16Array is the destination", function() {
if (typeof Int16Array !== 'undefined') {
var src = new Int16Array();
var dst = new Int16Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Int32Array is the destination", function() {
if (typeof Int32Array !== 'undefined') {
var src = new Int32Array();
var dst = new Int32Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Float32Array is the destination", function() {
if (typeof Float32Array !== 'undefined') {
var src = new Float32Array();
var dst = new Float32Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should throw an exception if a Float64Array is the destination", function() {
if (typeof Float64Array !== 'undefined') {
var src = new Float64Array();
var dst = new Float64Array(5);
expect(function() { copy(src, dst); })
.toThrowMinErr("ng", "cpta", "Can't copy! TypedArray destination cannot be mutated.");
}
});
it("should deeply copy an array into an existing array", function() {
var src = [1, {name:"value"}];
var dst = [{key:"v"}];
@@ -133,11 +313,19 @@ describe('angular', function() {
it('should throw an exception if a Scope is being copied', inject(function($rootScope) {
expect(function() { copy($rootScope.$new()); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
expect(function() { copy({child: $rootScope.$new()}, {}); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
expect(function() { copy([$rootScope.$new()]); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
}));
it('should throw an exception if a Window is being copied', function() {
expect(function() { copy(window); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
expect(function() { copy({child: window}); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
expect(function() { copy([window], []); }).
toThrowMinErr("ng", "cpws", "Can't copy! Making copies of Window or Scope instances is not supported.");
});
it('should throw an exception when source and destination are equivalent', function() {
@@ -154,9 +342,14 @@ describe('angular', function() {
hashKey(src);
dst = copy(src);
expect(hashKey(dst)).not.toEqual(hashKey(src));
src = {foo: {}};
hashKey(src.foo);
dst = copy(src);
expect(hashKey(src.foo)).not.toEqual(hashKey(dst.foo));
});
it('should retain the previous $$hashKey', function() {
it('should retain the previous $$hashKey when copying object with hashKey', function() {
var src,dst,h;
src = {};
dst = {};
@@ -171,7 +364,21 @@ describe('angular', function() {
expect(hashKey(dst)).toEqual(h);
});
it('should handle circular references when circularRefs is turned on', function() {
it('should retain the previous $$hashKey when copying non-object', function() {
var dst = {};
var h = hashKey(dst);
copy(null, dst);
expect(hashKey(dst)).toEqual(h);
copy(42, dst);
expect(hashKey(dst)).toEqual(h);
copy(new Date(), dst);
expect(hashKey(dst)).toEqual(h);
});
it('should handle circular references', function() {
var a = {b: {a: null}, self: null, selfs: [null, null, [null]]};
a.b.a = a;
a.self = a;
@@ -182,13 +389,87 @@ describe('angular', function() {
expect(aCopy).not.toBe(a);
expect(aCopy).toBe(aCopy.self);
expect(aCopy).toBe(aCopy.selfs[2][0]);
expect(aCopy.selfs[2]).not.toBe(a.selfs[2]);
var copyTo = [];
aCopy = copy(a, copyTo);
expect(aCopy).toBe(copyTo);
expect(aCopy).not.toBe(a);
expect(aCopy).toBe(aCopy.self);
});
it('should deeply copy XML nodes', function() {
var anElement = document.createElement('foo');
anElement.appendChild(document.createElement('bar'));
var theCopy = anElement.cloneNode(true);
expect(copy(anElement).outerHTML).toEqual(theCopy.outerHTML);
expect(copy(anElement)).not.toBe(anElement);
});
it('should not try to call a non-function called `cloneNode`', function() {
expect(copy.bind(null, { cloneNode: 100 })).not.toThrow();
});
it('should handle objects with multiple references', function() {
var b = {};
var a = [b, -1, b];
var aCopy = copy(a);
expect(aCopy[0]).not.toBe(a[0]);
expect(aCopy[0]).toBe(aCopy[2]);
var copyTo = [];
aCopy = copy(a, copyTo);
expect(aCopy).toBe(copyTo);
expect(aCopy[0]).not.toBe(a[0]);
expect(aCopy[0]).toBe(aCopy[2]);
});
it('should handle date/regex objects with multiple references', function() {
var re = /foo/;
var d = new Date();
var o = {re: re, re2: re, d: d, d2: d};
var oCopy = copy(o);
expect(oCopy.re).toBe(oCopy.re2);
expect(oCopy.d).toBe(oCopy.d2);
oCopy = copy(o, {});
expect(oCopy.re).toBe(oCopy.re2);
expect(oCopy.d).toBe(oCopy.d2);
});
it('should clear destination arrays correctly when source is non-array', function() {
expect(copy(null, [1,2,3])).toEqual([]);
expect(copy(undefined, [1,2,3])).toEqual([]);
expect(copy({0: 1, 1: 2}, [1,2,3])).toEqual([1,2]);
expect(copy(new Date(), [1,2,3])).toEqual([]);
expect(copy(/a/, [1,2,3])).toEqual([]);
expect(copy(true, [1,2,3])).toEqual([]);
});
it('should clear destination objects correctly when source is non-array', function() {
expect(copy(null, {0:1,1:2,2:3})).toEqual({});
expect(copy(undefined, {0:1,1:2,2:3})).toEqual({});
expect(copy(new Date(), {0:1,1:2,2:3})).toEqual({});
expect(copy(/a/, {0:1,1:2,2:3})).toEqual({});
expect(copy(true, {0:1,1:2,2:3})).toEqual({});
});
it('should copy objects with no prototype parent', function() {
var obj = extend(Object.create(null), {
a: 1,
b: 2,
c: 3
});
var dest = copy(obj);
expect(Object.getPrototypeOf(dest)).toBe(null);
expect(dest.a).toBe(1);
expect(dest.b).toBe(2);
expect(dest.c).toBe(3);
expect(Object.keys(dest)).toEqual(['a', 'b', 'c']);
});
});
+10
View File
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html ng-app>
<body>
<form>
<input type="hidden" value="{{value}}" />
<button ng-click="value = '{{ 7 * 6 }}'">Click me</button>
</form>
<script src="angular.js"></script>
</body>
</html>
+15
View File
@@ -0,0 +1,15 @@
describe('hidden thingy', function() {
it('should pass', function() {
loadFixture('input-hidden');
expect(element(by.css('input')).getAttribute('value')).toEqual('');
element(by.css('button')).click();
expect(element(by.css('input')).getAttribute('value')).toEqual('{{ 7 * 6 }}');
loadFixture('sample');
browser.driver.executeScript('history.back()');
var expectedValue = browser.params.browser === 'safari' ? '{{ 7 * 6 }}' : '';
expect(element(by.css('input')).getAttribute('value')).toEqual(expectedValue);
});
});
+9
View File
@@ -56,3 +56,12 @@ function createMockStyleSheet(doc, wind) {
}
};
}
function browserSupportsCssAnimations() {
var nav = window.navigator.appVersion;
if (nav.indexOf('MSIE') >= 0) {
var version = parseInt(navigator.appVersion.match(/MSIE ([\d.]+)/)[1]);
return version >= 10; //only IE10+ support keyframes / transitions
}
return true;
}
-10
View File
@@ -147,7 +147,6 @@ describe('$compile', function() {
describe('configuration', function() {
it('should register a directive', function() {
module(function() {
directive('div', function(log) {
@@ -202,15 +201,6 @@ describe('$compile', function() {
});
inject(function($compile) {});
});
it('should throw an exception if a directive name starts with a non-lowercase letter', function() {
module(function() {
expect(function() {
directive('BadDirectiveName', function() { });
}).toThrowMinErr('$compile','baddir', "Directive name 'BadDirectiveName' is invalid. The first character must be a lowercase letter");
});
inject(function($compile) {});
});
});
+1 -1
View File
@@ -472,7 +472,7 @@ describe('ngClass animations', function() {
//is spaced-out then it is required so that the original digestion
//is kicked into gear
$rootScope.$digest();
$animate.triggerCallbacks();
$animate.flush();
expect(element.data('state')).toBe('crazy-enter');
expect(enterComplete).toBe(true);
+7 -7
View File
@@ -433,7 +433,7 @@ describe('ngInclude', function() {
expect(autoScrollSpy).not.toHaveBeenCalled();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).toHaveBeenCalledOnce();
}));
@@ -450,7 +450,7 @@ describe('ngInclude', function() {
});
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
$rootScope.$apply(function() {
$rootScope.tpl = 'another.html';
@@ -459,7 +459,7 @@ describe('ngInclude', function() {
expect($animate.queue.shift().event).toBe('leave');
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
$rootScope.$apply(function() {
$rootScope.tpl = 'template.html';
@@ -468,7 +468,7 @@ describe('ngInclude', function() {
expect($animate.queue.shift().event).toBe('leave');
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).toHaveBeenCalled();
expect(autoScrollSpy.callCount).toBe(3);
@@ -484,7 +484,7 @@ describe('ngInclude', function() {
});
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).not.toHaveBeenCalled();
}));
@@ -500,7 +500,7 @@ describe('ngInclude', function() {
});
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
$rootScope.$apply(function() {
$rootScope.tpl = 'template.html';
@@ -522,7 +522,7 @@ describe('ngInclude', function() {
$rootScope.$apply("tpl = 'template.html'");
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).toHaveBeenCalledOnce();
}
+1 -1
View File
@@ -904,7 +904,7 @@ describe('ngModel', function() {
expect(function() {
scope.$apply('value = "123"');
}).toThrowMinErr("ngModel", "$asyncValidators",
}).toThrowMinErr("ngModel", "nopromise",
"Expected asynchronous validator to return a promise but got 'true' instead.");
}));
+1 -1
View File
@@ -1486,7 +1486,7 @@ describe('ngRepeat animations', function() {
$rootScope.$digest();
expect(element.text()).toBe('123'); // the original order should be preserved
$animate.triggerReflow();
$animate.flush();
$timeout.flush(1500); // 1s * 1.5 closing buffer
expect(element.text()).toBe('13');
+15
View File
@@ -204,6 +204,21 @@ describe('validators', function() {
expect($rootScope.form.test.$error.pattern).toBe(true);
expect(inputElm).not.toBeValid();
});
it('should validate the viewValue and not the modelValue', function() {
var inputElm = helper.compileInput('<input type="text" name="test" ng-model="value" pattern="\\d{4}">');
var ctrl = inputElm.controller('ngModel');
ctrl.$parsers.push(function(value) {
return (value * 10) + '';
});
helper.changeInputValueTo('1234');
expect($rootScope.form.test.$error.pattern).not.toBe(true);
expect($rootScope.form.test.$modelValue).toBe('12340');
expect(inputElm).toBeValid();
});
});
+1 -1
View File
@@ -235,7 +235,7 @@ describe('$httpBackend', function() {
it('should read responseText if response was not defined', function() {
// old browsers like IE8, don't support responseType, so they always respond with responseText
// old browsers like IE9, don't support responseType, so they always respond with responseText
$backend('GET', '/whatever', null, callback, {}, null, null, 'blob');
+1 -1
View File
@@ -1657,7 +1657,7 @@ describe('$http', function() {
$http({method: 'GET', url: '/some', timeout: canceler.promise}).error(
function(data, status, headers, config) {
expect(data).toBeUndefined();
expect(status).toBe(0);
expect(status).toBe(-1);
expect(headers()).toEqual({});
expect(config.url).toBe('/some');
callback();
+49 -35
View File
@@ -46,7 +46,7 @@ describe('$location', function() {
it('should not include the drive name in path() on WIN', function() {
//See issue #4680 for details
var locationUrl = new LocationHashbangUrl('file:///base', '#!');
var locationUrl = new LocationHashbangUrl('file:///base', 'file:///', '#!');
locationUrl.$$parse('file:///base#!/foo?a=b&c#hash');
expect(locationUrl.path()).toBe('/foo');
@@ -54,7 +54,7 @@ describe('$location', function() {
it('should include the drive name if it was provided in the input url', function() {
var locationUrl = new LocationHashbangUrl('file:///base', '#!');
var locationUrl = new LocationHashbangUrl('file:///base', 'file:///', '#!');
locationUrl.$$parse('file:///base#!/C:/foo?a=b&c#hash');
expect(locationUrl.path()).toBe('/C:/foo');
@@ -64,7 +64,7 @@ describe('$location', function() {
describe('NewUrl', function() {
function createLocationHtml5Url() {
var locationUrl = new LocationHtml5Url('http://www.domain.com:9877/');
var locationUrl = new LocationHtml5Url('http://www.domain.com:9877/', 'http://www.domain.com:9877/');
locationUrl.$$parse('http://www.domain.com:9877/path/b?search=a&b=c&d#hash');
return locationUrl;
}
@@ -299,18 +299,18 @@ describe('$location', function() {
it('should parse new url', function() {
var locationUrl = new LocationHtml5Url('http://host.com/');
var locationUrl = new LocationHtml5Url('http://host.com/', 'http://host.com/');
locationUrl.$$parse('http://host.com/base');
expect(locationUrl.path()).toBe('/base');
locationUrl = new LocationHtml5Url('http://host.com/');
locationUrl = new LocationHtml5Url('http://host.com/', 'http://host.com/');
locationUrl.$$parse('http://host.com/base#');
expect(locationUrl.path()).toBe('/base');
});
it('should prefix path with forward-slash', function() {
var locationUrl = new LocationHtml5Url('http://server/');
var locationUrl = new LocationHtml5Url('http://server/', 'http://server/') ;
locationUrl.path('b');
expect(locationUrl.path()).toBe('/b');
@@ -319,7 +319,7 @@ describe('$location', function() {
it('should set path to forward-slash when empty', function() {
var locationUrl = new LocationHtml5Url('http://server/');
var locationUrl = new LocationHtml5Url('http://server/', 'http://server/') ;
locationUrl.$$parse('http://server/');
expect(locationUrl.path()).toBe('/');
expect(locationUrl.absUrl()).toBe('http://server/');
@@ -356,7 +356,7 @@ describe('$location', function() {
});
it('should prepend path with basePath', function() {
var locationUrl = new LocationHtml5Url('http://server/base/');
var locationUrl = new LocationHtml5Url('http://server/base/', 'http://server/base/') ;
locationUrl.$$parse('http://server/base/abc?a');
expect(locationUrl.path()).toBe('/abc');
expect(locationUrl.search()).toEqual({a: true});
@@ -367,7 +367,7 @@ describe('$location', function() {
it('should throw error when invalid server url given', function() {
var locationUrl = new LocationHtml5Url('http://server.org/base/abc', '/base');
var locationUrl = new LocationHtml5Url('http://server.org/base/abc', 'http://server.org/base/', '/base');
expect(function() {
locationUrl.$$parse('http://other.server.org/path#/path');
@@ -376,7 +376,7 @@ describe('$location', function() {
it('should throw error when invalid base url given', function() {
var locationUrl = new LocationHtml5Url('http://server.org/base/abc', '/base');
var locationUrl = new LocationHtml5Url('http://server.org/base/abc', 'http://server.org/base/', '/base');
expect(function() {
locationUrl.$$parse('http://server.org/path#/path');
@@ -444,7 +444,7 @@ describe('$location', function() {
it('should decode special characters', function() {
var locationUrl = new LocationHtml5Url('http://host.com/');
var locationUrl = new LocationHtml5Url('http://host.com/', 'http://host.com/');
locationUrl.$$parse('http://host.com/a%20%3C%3E%23?i%20j=%3C%3E%23#x%20%3C%3E%23');
expect(locationUrl.path()).toBe('/a <>#');
expect(locationUrl.search()).toEqual({'i j': '<>#'});
@@ -452,7 +452,7 @@ describe('$location', function() {
});
it('should decode pluses as spaces in urls', function() {
var locationUrl = new LocationHtml5Url('http://host.com/');
var locationUrl = new LocationHtml5Url('http://host.com/', 'http://host.com/');
locationUrl.$$parse('http://host.com/?a+b=c+d');
expect(locationUrl.search()).toEqual({'a b':'c d'});
});
@@ -470,7 +470,7 @@ describe('$location', function() {
describe('HashbangUrl', function() {
function createHashbangUrl() {
var locationUrl = new LocationHashbangUrl('http://www.server.org:1234/base', '#!');
var locationUrl = new LocationHashbangUrl('http://www.server.org:1234/base', 'http://www.server.org:1234/', '#!');
locationUrl.$$parse('http://www.server.org:1234/base#!/path?a=b&c#hash');
return locationUrl;
}
@@ -499,7 +499,7 @@ describe('$location', function() {
it('should preserve query params in base', function() {
var locationUrl = new LocationHashbangUrl('http://www.server.org:1234/base?base=param', '#');
var locationUrl = new LocationHashbangUrl('http://www.server.org:1234/base?base=param', 'http://www.server.org:1234/', '#');
locationUrl.$$parse('http://www.server.org:1234/base?base=param#/path?a=b&c#hash');
expect(locationUrl.absUrl()).toBe('http://www.server.org:1234/base?base=param#/path?a=b&c#hash');
@@ -511,7 +511,7 @@ describe('$location', function() {
it('should prefix path with forward-slash', function() {
var locationUrl = new LocationHashbangUrl('http://host.com/base', '#');
var locationUrl = new LocationHashbangUrl('http://host.com/base', 'http://host.com/', '#');
locationUrl.$$parse('http://host.com/base#path');
expect(locationUrl.path()).toBe('/path');
expect(locationUrl.absUrl()).toBe('http://host.com/base#/path');
@@ -523,7 +523,7 @@ describe('$location', function() {
it('should set path to forward-slash when empty', function() {
var locationUrl = new LocationHashbangUrl('http://server/base', '#!');
var locationUrl = new LocationHashbangUrl('http://server/base', 'http://server/', '#!');
locationUrl.$$parse('http://server/base');
locationUrl.path('aaa');
@@ -592,7 +592,7 @@ describe('$location', function() {
it('should decode special characters', function() {
var locationUrl = new LocationHashbangUrl('http://host.com/a', '#');
var locationUrl = new LocationHashbangUrl('http://host.com/a', 'http://host.com/', '#');
locationUrl.$$parse('http://host.com/a#/%20%3C%3E%23?i%20j=%3C%3E%23#x%20%3C%3E%23');
expect(locationUrl.path()).toBe('/ <>#');
expect(locationUrl.search()).toEqual({'i j': '<>#'});
@@ -601,35 +601,35 @@ describe('$location', function() {
it('should return decoded characters for search specified in URL', function() {
var locationUrl = new LocationHtml5Url('http://host.com/');
var locationUrl = new LocationHtml5Url('http://host.com/', 'http://host.com/');
locationUrl.$$parse('http://host.com/?q=1%2F2%203');
expect(locationUrl.search()).toEqual({'q': '1/2 3'});
});
it('should return decoded characters for search specified with setter', function() {
var locationUrl = new LocationHtml5Url('http://host.com/');
var locationUrl = new LocationHtml5Url('http://host.com/', 'http://host.com/');
locationUrl.$$parse('http://host.com/');
locationUrl.search('q', '1/2 3');
expect(locationUrl.search()).toEqual({'q': '1/2 3'});
});
it('should return an array for duplicate params', function() {
var locationUrl = new LocationHtml5Url('http://host.com');
var locationUrl = new LocationHtml5Url('http://host.com', 'http://host.com') ;
locationUrl.$$parse('http://host.com');
locationUrl.search('q', ['1/2 3','4/5 6']);
expect(locationUrl.search()).toEqual({'q': ['1/2 3','4/5 6']});
});
it('should encode an array correctly from search and add to url', function() {
var locationUrl = new LocationHtml5Url('http://host.com');
var locationUrl = new LocationHtml5Url('http://host.com', 'http://host.com') ;
locationUrl.$$parse('http://host.com');
locationUrl.search({'q': ['1/2 3','4/5 6']});
expect(locationUrl.absUrl()).toEqual('http://host.com?q=1%2F2%203&q=4%2F5%206');
});
it('should rewrite params when specifing a single param in search', function() {
var locationUrl = new LocationHtml5Url('http://host.com');
var locationUrl = new LocationHtml5Url('http://host.com', 'http://host.com') ;
locationUrl.$$parse('http://host.com');
locationUrl.search({'q': '1/2 3'});
expect(locationUrl.absUrl()).toEqual('http://host.com?q=1%2F2%203');
@@ -860,7 +860,6 @@ describe('$location', function() {
});
});
// location.href = '...' fires hashchange event synchronously, so it might happen inside $apply
it('should not $apply when browser url changed inside $apply', function() {
initService({html5Mode:false,hashPrefix: '!',supportHistory: true});
@@ -1150,6 +1149,19 @@ describe('$location', function() {
expect($browserUrl.mostRecentCall.args).toEqual(['http://new.com/a/b/bar', false, null]);
});
});
it('should force a page reload if navigating outside of the application base href', function() {
initService({html5Mode:true, supportHistory: true});
mockUpBrowser({initialUrl:'http://new.com/a/b/', baseHref:'/a/b/'});
inject(function($window, $browser, $location) {
$window.location.href = 'http://new.com/a/outside.html';
spyOn($window.location, '$$setHref');
expect($window.location.$$setHref).not.toHaveBeenCalled();
$browser.$$checkUrlChange();
expect($window.location.$$setHref).toHaveBeenCalledWith('http://new.com/a/outside.html');
});
});
});
@@ -2353,8 +2365,8 @@ describe('$location', function() {
var locationUrl, locationIndexUrl;
beforeEach(function() {
locationUrl = new LocationHtml5Url('http://server/pre/', 'http://server/pre/path');
locationIndexUrl = new LocationHtml5Url('http://server/pre/index.html', 'http://server/pre/path');
locationUrl = new LocationHtml5Url('http://server/pre/', 'http://server/pre/', 'http://server/pre/path');
locationIndexUrl = new LocationHtml5Url('http://server/pre/index.html', 'http://server/pre/', 'http://server/pre/path');
});
it('should rewrite URL', function() {
@@ -2416,7 +2428,7 @@ describe('$location', function() {
it('should rewrite URL', function() {
/* jshint scripturl: true */
locationUrl = new LocationHashbangUrl('http://server/pre/', '#');
locationUrl = new LocationHashbangUrl('http://server/pre/', 'http://server/pre/', '#');
expect(parseLinkAndReturn(locationUrl, 'http://other')).toEqual(undefined);
expect(parseLinkAndReturn(locationUrl, 'http://server/pre/')).toEqual('http://server/pre/');
@@ -2425,7 +2437,7 @@ describe('$location', function() {
});
it("should not set hash if one was not originally specified", function() {
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', '#');
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', 'http://server/pre/', '#');
locationUrl.$$parse('http://server/pre/index.html');
expect(locationUrl.url()).toBe('');
@@ -2433,7 +2445,7 @@ describe('$location', function() {
});
it("should parse hash if one was specified", function() {
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', '#');
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', 'http://server/pre/', '#');
locationUrl.$$parse('http://server/pre/index.html#/foo/bar');
expect(locationUrl.url()).toBe('/foo/bar');
@@ -2442,7 +2454,7 @@ describe('$location', function() {
it("should prefix hash url with / if one was originally missing", function() {
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', '#');
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', 'http://server/pre/', '#');
locationUrl.$$parse('http://server/pre/index.html#not-starting-with-slash');
expect(locationUrl.url()).toBe('/not-starting-with-slash');
@@ -2452,7 +2464,7 @@ describe('$location', function() {
it('should not strip stuff from path just because it looks like Windows drive when it\'s not',
function() {
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', '#');
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', 'http://server/pre/', '#');
locationUrl.$$parse('http://server/pre/index.html#http%3A%2F%2Fexample.com%2F');
expect(locationUrl.url()).toBe('/http://example.com/');
@@ -2464,7 +2476,7 @@ describe('$location', function() {
});
it('should allow navigating outside the original base URL', function() {
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', '#');
locationUrl = new LocationHashbangUrl('http://server/pre/index.html', 'http://server/pre/', '#');
locationUrl.$$parse('http://server/next/index.html');
expect(locationUrl.url()).toBe('');
@@ -2478,8 +2490,8 @@ describe('$location', function() {
var locationUrl, locationIndexUrl;
beforeEach(function() {
locationUrl = new LocationHashbangInHtml5Url('http://server/pre/', '#!');
locationIndexUrl = new LocationHashbangInHtml5Url('http://server/pre/index.html', '#!');
locationUrl = new LocationHashbangInHtml5Url('http://server/pre/', 'http://server/pre/', '#!');
locationIndexUrl = new LocationHashbangInHtml5Url('http://server/pre/index.html', 'http://server/pre/', '#!');
});
it('should rewrite URL', function() {
@@ -2540,8 +2552,10 @@ describe('$location', function() {
win.addEventListener = angular.noop;
win.removeEventListener = angular.noop;
win.location = {
get href() { return parser.href; },
set href(val) { parser.href = val; },
get href() { return this.$$getHref(); },
$$getHref: function() { return parser.href; },
set href(val) { this.$$setHref(val); },
$$setHref: function(val) { parser.href = val; },
get hash() { return parser.hash; },
// The parser correctly strips on a single preceding hash character if necessary
// before joining the fragment onto the href by a new hash character
+23
View File
@@ -1190,6 +1190,20 @@ describe('parser', function() {
scope.$eval('{}["__proto__"].foo = 1');
}).toThrowMinErr('$parse', 'isecfld');
expect(function() {
scope.$eval('{}[["__proto__"]]');
}).toThrowMinErr('$parse', 'isecfld');
expect(function() {
scope.$eval('{}[["__proto__"]].foo = 1');
}).toThrowMinErr('$parse', 'isecfld');
expect(function() {
scope.$eval('0[["__proto__"]]');
}).toThrowMinErr('$parse', 'isecfld');
expect(function() {
scope.$eval('0[["__proto__"]].foo = 1');
}).toThrowMinErr('$parse', 'isecfld');
scope.a = "__pro";
scope.b = "to__";
expect(function() {
@@ -1201,6 +1215,15 @@ describe('parser', function() {
});
});
it('should prevent the exploit', function() {
expect(function() {
scope.$eval('(1)[{0: "__proto__", 1: "__proto__", 2: "__proto__", 3: "safe", length: 4, toString: [].pop}].foo = 1');
});
if (!msie || msie > 10) {
expect((1)['__proto__'].foo).toBeUndefined();
}
});
it('should prevent the exploit', function() {
expect(function() {
scope.$eval('' +
-6
View File
@@ -2,12 +2,6 @@
describe('SCE', function() {
// Work around an IE8 bug. Though window.inject === angular.mock.inject, if it's invoked the
// window scope, IE8 loses the exception object that bubbles up and replaces it with a TypeError.
// By using a local alias, it gets invoked on the global scope instead of window.
// Ref: https://github.com/angular/angular.js/pull/4221#/issuecomment-25515813
var inject = angular.mock.inject;
describe('when disabled', function() {
beforeEach(function() {
module(function($sceProvider) {
File diff suppressed because it is too large Load Diff
+179 -6
View File
@@ -719,8 +719,6 @@ describe('ngMock', function() {
it('should serialize scope that has overridden "hasOwnProperty"', inject(function($rootScope, $sniffer) {
/* jshint -W001 */
// MS IE8 just doesn't work for this kind of thing, since "for ... in" doesn't return
// things like hasOwnProperty even if it is explicitly defined on the actual object!
$rootScope.hasOwnProperty = 'X';
expect(d($rootScope)).toMatch(/Scope\(.*\): \{/);
expect(d($rootScope)).toMatch(/hasOwnProperty: "X"/);
@@ -906,10 +904,6 @@ describe('ngMock', function() {
});
// We don't run the following tests on IE8.
// IE8 throws "Object does not support this property or method." error,
// when thrown from a function defined on window (which `inject` is).
it('should not change thrown Errors', inject(function($sniffer) {
expect(function() {
inject(function() {
@@ -1834,6 +1828,185 @@ describe('ngMockE2E', function() {
}));
});
});
describe('ngAnimateMock', function() {
beforeEach(module('ngAnimate'));
beforeEach(module('ngAnimateMock'));
var ss, element, trackedAnimations;
beforeEach(module(function($animateProvider) {
trackedAnimations = [];
$animateProvider.register('.animate', function($timeout) {
return {
leave: logFn('leave'),
addClass: logFn('addClass')
};
function logFn(method) {
return function(element) {
trackedAnimations.push(getDoneCallback(arguments));
// this will never finish an animation so we'll issue a call
// to timeout so that the mock driver won't throw an exception
$timeout(angular.noop, 0, false);
};
}
function getDoneCallback(args) {
for (var i = args.length; i > 0; i--) {
if (angular.isFunction(args[i])) return args[i];
}
}
});
return function($animate, $rootElement, $document, $rootScope, $window) {
if (ss) {
ss.destroy();
}
ss = createMockStyleSheet($document, $window);
element = angular.element('<div class="animate"></div>');
$rootElement.append(element);
angular.element($document[0].body).append($rootElement);
$animate.enabled(true);
$rootScope.$digest();
};
}));
describe('$animate.queue', function() {
it('should maintain a queue of the executed animations', inject(function($animate) {
element.removeClass('animate'); // we don't care to test any actual animations
var options = {};
$animate.addClass(element, 'on', options);
var first = $animate.queue[0];
expect(first.element).toBe(element);
expect(first.event).toBe('addClass');
expect(first.options).toBe(options);
$animate.removeClass(element, 'off', options);
var second = $animate.queue[1];
expect(second.element).toBe(element);
expect(second.event).toBe('removeClass');
expect(second.options).toBe(options);
$animate.leave(element, options);
var third = $animate.queue[2];
expect(third.element).toBe(element);
expect(third.event).toBe('leave');
expect(third.options).toBe(options);
}));
});
describe('$animate.flush()', function() {
it('should throw an error if there is nothing to animate', inject(function($animate) {
expect(function() {
$animate.flush();
}).toThrow('No pending animations ready to be closed or flushed');
}));
it('should trigger the animation to start',
inject(function($animate) {
expect(trackedAnimations.length).toBe(0);
$animate.leave(element);
$animate.flush();
expect(trackedAnimations.length).toBe(1);
}));
it('should trigger the animation to end once run and called',
inject(function($animate) {
$animate.leave(element);
$animate.flush();
expect(element.parent().length).toBe(1);
trackedAnimations[0]();
$animate.flush();
expect(element.parent().length).toBe(0);
}));
it('should trigger the animation promise callback to fire once run and closed',
inject(function($animate) {
var doneSpy = jasmine.createSpy();
$animate.leave(element).then(doneSpy);
$animate.flush();
trackedAnimations[0]();
expect(doneSpy).not.toHaveBeenCalled();
$animate.flush();
expect(doneSpy).toHaveBeenCalled();
}));
it('should trigger a series of CSS animations to trigger and start once run',
inject(function($animate, $rootScope) {
if (!browserSupportsCssAnimations()) return;
ss.addRule('.leave-me.ng-leave', 'transition:1s linear all;');
var i, elm, elms = [];
for (i = 0; i < 5; i++) {
elm = angular.element('<div class="leave-me"></div>');
element.append(elm);
elms.push(elm);
$animate.leave(elm);
}
$rootScope.$digest();
for (i = 0; i < 5; i++) {
elm = elms[i];
expect(elm.hasClass('ng-leave')).toBe(true);
expect(elm.hasClass('ng-leave-active')).toBe(false);
}
$animate.flush();
for (i = 0; i < 5; i++) {
elm = elms[i];
expect(elm.hasClass('ng-leave')).toBe(true);
expect(elm.hasClass('ng-leave-active')).toBe(true);
}
}));
it('should trigger parent and child animations to run within the same flush',
inject(function($animate, $rootScope) {
var child = angular.element('<div class="animate child"></div>');
element.append(child);
expect(trackedAnimations.length).toBe(0);
$animate.addClass(element, 'go');
$animate.addClass(child, 'start');
$animate.flush();
expect(trackedAnimations.length).toBe(2);
}));
it('should trigger animation callbacks when called',
inject(function($animate, $rootScope) {
var spy = jasmine.createSpy();
element.on('$animate:before', spy);
element.on('$animate:close', spy);
$animate.addClass(element, 'on');
expect(spy).not.toHaveBeenCalled();
$animate.flush();
expect(spy.callCount).toBe(1);
trackedAnimations[0]();
$animate.flush();
expect(spy.callCount).toBe(2);
}));
});
});
});
describe('make sure that we can create an injector outside of tests', function() {
+8 -9
View File
@@ -501,8 +501,7 @@ describe('ngView', function() {
$location.url('/foo');
$rootScope.$digest();
// using toMatch because in IE8+jquery the space doesn't get preserved. jquery bug?
expect(element.text()).toMatch(/\s*WORKS/);
expect(element.text()).toEqual(' \n WORKS');
var div = element.find('div');
expect(div.parent()[0].nodeName.toUpperCase()).toBeOneOf('NG:VIEW', 'VIEW');
@@ -713,7 +712,7 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
$animate.triggerCallbacks();
$animate.flush();
$location.path('/');
$rootScope.$digest();
@@ -777,7 +776,7 @@ describe('ngView animations', function() {
expect($animate.queue.shift().event).toBe('addClass');
expect($animate.queue.shift().event).toBe('removeClass');
$animate.triggerReflow();
$animate.flush();
expect(item.hasClass('classy')).toBe(false);
expect(item.hasClass('boring')).toBe(true);
@@ -915,7 +914,7 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).toHaveBeenCalledOnce();
}));
@@ -929,7 +928,7 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).toHaveBeenCalledOnce();
}));
@@ -942,7 +941,7 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).not.toHaveBeenCalled();
}));
@@ -956,7 +955,7 @@ describe('ngView animations', function() {
$location.path('/foo');
$rootScope.$digest();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect(autoScrollSpy).not.toHaveBeenCalled();
}));
@@ -973,7 +972,7 @@ describe('ngView animations', function() {
expect(autoScrollSpy).not.toHaveBeenCalled();
expect($animate.queue.shift().event).toBe('enter');
$animate.triggerCallbacks();
$animate.flush();
expect($animate.enter).toHaveBeenCalledOnce();
expect(autoScrollSpy).toHaveBeenCalledOnce();