Compare commits

...

96 Commits

Author SHA1 Message Date
Peter Bacon Darwin f276ad0d51 docs(CHANGELOG): add release notes for 1.5.4 2016-04-14 09:13:48 +01:00
Pete Bacon Darwin 33f817b99c feat($componentController): provide isolated scope if none is passed (#14425)
Closes #14425
2016-04-13 18:34:39 +01:00
Martin Staffa 6a4124d0fb perf(ngOptions): use documentFragment to populate select
This changes the way option elements are generated when the ngOption collection changes.
Previously, we would re-use option elements when possible (updating their text and
label). Now, we first remove all currently displayed options and the create new options for the
collection. The new options are first appended to a documentFragment, which is in the end appended
to the selectElement.

Using documentFragment improves render performance in IE with large option collections
(> 100 elements) considerably.

Creating new options from scratch fixes issues in IE where the select would become unresponsive
to user input.

Fixes #13607
Fixes #13239
Fixes #12076
2016-04-13 18:42:10 +02:00
Georgios Kalpakas 26d1b34321 docs(ngView): add known issue about asynchronously loaded ngView
Closes #14424
2016-04-13 18:45:40 +03:00
Georgios Kalpakas 7fba6b603f revert: "fix(ngRoute): allow ngView to be included in an asynchronously loaded template"
This reverts commit 5e37b2a7fd.
Eagerly loading `$route`, could break tests, because it might request the root or default route
template (something `$httpBackend` would know nothing about).

It will be re-applied for `v1.6.x`, with a breaking change notice and possibly a way to disable
the feature is tests.

Fixes #14337
2016-04-13 18:45:06 +03:00
Jason Bedard c115b37c33 perf($compile): use createMap() for directive bindings to allow fast forEach
Closes #12529
2016-04-13 12:05:30 +03:00
Georgii Dolzhykov d20ba95e28 docs(Module): fix parameter names for .decorator()
Closes #14413
2016-04-12 14:52:18 +03:00
aortyl 7945e5010a docs(guide/scope): add comma for readability
Closes #14411
2016-04-11 22:22:36 +03:00
Jason Bedard 21d148aedc refactor($compile): move setting of controller data to single location
Closes #13421
2016-04-11 18:54:41 +01:00
Georgios Kalpakas 4c8aeefb62 fix($compile): do not use noop() as controller for multiple components
Currently, custom annotations are copied from the CDO onto the controller constructor.
Using `noop()` when no controller has been specified, pollutes it with custom annotations and
makes one component's annotations available to all other components that have `noop()` as their
controller.

Fixes #14391
Closes #14402
2016-04-11 18:43:17 +01:00
a510 d384834fde fix($injector): ensure functions with overridden toString() are annotated properly
Closes #14361
2016-04-11 13:03:56 +03:00
David Rodenas Pico f975d8d448 fix(ngClass): fix watching of an array expression containing an object
Closes #14405
2016-04-11 12:40:36 +03:00
Georgios Kalpakas 2602daf993 refactor($compile): remove unnecessary call to isDefined()
(As discussed in https://github.com/angular/angular.js/pull/14406/files#r59131398.)
2016-04-11 12:33:07 +03:00
Gene McCulley 9f681c459a docs(numberFilter): fix the description of the returned value
Closes #14408
2016-04-11 12:28:08 +03:00
Peter Bacon Darwin d9448dcb9f fix($compile): still trigger $onChanges even if the inner value already matches the new value
Closes #14406
2016-04-10 20:31:01 +01:00
Jurko Gospodnetić e9c718a465 fix(ngMock): fix collecting stack trace in inject() on IE10+, PhantomJS
Add support for collecting current stack trace information in browsers
(e.g. IE10+, PhantomJS) that do not automatically store the current stack trace
information in a newly created `Error` object's `stack` property, but
only add it there once the `Error` gets thrown.

The original implementation works fine in Firefox & Chrome, but fails on IE10+
and PhantomJS where it, for example, breaks Karma's error reporting in cases
when an exception is thrown in a test like the following:

```
it('the holy crusade', inject(function() {
  var x = {};
  x.holyGrail();
}));
```

In this case, the ngMock `inject()` implementation would incorrectly add the
word `undefined` at the end of the collected error stack trace information,
thus causing the main error description to be reported back to Karma as
`undefined`.

The added test makes sure this functionality:

- works as expected in browsers supporting JavaScript stack trace
  collection, e.g. Chrome, Firefox, IE10+, Opera & PhantomJS
- does not add any bogus stack track information in browsers that do
  not support JavaScript stack trace collection, e.g. IE9

Fixes #13591
Closes #13592

Closes #13593
2016-04-09 20:51:22 +03:00
Georgios Kalpakas 4dc44f7205 test(helpers): fix error message generation for toHaveBeenCalledOnce[With] matchers
Jasmine 2.4 (maybe earlier) does not support returning an array containing both the "normal" and the
negative error messages. It will always concatenate them.

Closes #14275
2016-04-09 17:06:47 +03:00
Jan Niehusmann 499e1b2adf fix($compile): handle boolean attributes in @ bindings
Commit db5e0ff handles initial values of boolean attributes. The same
change needs to be applied inside the attrs.$observe() call.

Closes #14070
2016-04-09 16:53:41 +03:00
Peter Bacon Darwin 28c6c4dae2 docs(jqlite): add known issue
Closes #14251
2016-04-08 21:27:21 +01:00
Peter Bacon Darwin 791148a328 chore(package.json): update dgeni-packages to 0.12.0
This gives us `@knownissues` tags
2016-04-08 21:27:05 +01:00
Peter Bacon Darwin 01b1845088 feat($http): support handling additional XHR events
Closes #14367
Closes #11547
Closes #1934
2016-04-08 19:16:56 +01:00
Chris Chua 56c861c9e1 feat($http): support handling additional XHR events
Closes #11547
Closes #1934
2016-04-08 19:16:49 +01:00
Martin Staffa 451e0f6175 docs(README): fix typo 2016-04-08 19:00:20 +02:00
Martin Staffa aee7f1c72f docs(guide/accessibility): make jshint happy 2016-04-08 18:55:22 +02:00
Pablo Iván G. Soto 5fe28a0422 docs(README): add https links, improve style 2016-04-08 18:37:04 +02:00
Martin Staffa f56586d9a9 docs(guide/Accessibility): fix markdown errors, tweak layout 2016-04-08 17:32:13 +02:00
mohamed amr ec0baadcb6 feat(ngAria): add support for aria-readonly based on ngReadonly
Closes #14140
Closes #14077
2016-04-08 17:32:12 +02:00
Martin Staffa 9147d5c04a docs(ngComponentRouter): add note about shims needed for IE 2016-04-08 17:32:12 +02:00
Josh cb801378c9 docs(ngComponentRouter): fix typo
Simple typo fix from `betweent he` to `between the`

Closes #14396
2016-04-08 15:47:01 +02:00
Martin Staffa 79604f4628 fix(ngAnimate): remove event listeners only after all listeners have been called
The fix for removing the event callbacks on destroy introduced in
ce7f400011 removed the events too early, so that the event callbacks
for the "close" phase in "leave" animations were not called.

This commit fixes the behavior so that the event callbacks are only removed during on('$destroy')
when no animation is currently active on the element. When an animation is active, the event callbacks
will be removed after all callbacks have run, and if the element has no parent (has been removed from
the DOM).

Closes #14321
2016-04-08 15:47:00 +02:00
Martin Staffa bf6cb8ab0d feat(ngAnimate): let $animate.off() remove all listeners for an element 2016-04-08 15:46:59 +02:00
Andrew de74b3fd85 docs(guide/Components): fix small single letter typo
line 136: 'not' should be 'note'

Closes #14390
2016-04-08 01:32:00 +02:00
Martin Staffa c7a92d2a9a fix(ngAnimate): fire callbacks when document is hidden
Since commit a3a7afd3aa, animations are not run
when the document is hidden (only their structural or class change effects are executed).
However, some libraries rely on the $animate.on() callbacks to be called even when no actual animation
runs.
This commit restores the behavior for the ngAnimate.$animate functions.
Note that callbacks still won't be called if animations are disabled, because this would be be a potential
breaking change, as some applications might rely on this implementation.

Fixes #14120
2016-04-08 01:31:59 +02:00
Martin Staffa da284fc354 test(ngAnimate): test calling callbacks for various constellations 2016-04-08 01:31:58 +02:00
Martin Staffa 90da3059ce fix(ngAnimate): fire callbacks in the correct order for certain skipped animations 2016-04-08 01:31:58 +02:00
SHAHRUKH-KHAN 651fd05eca docs(angular.equals): add example
This Pull requests improves the doc by adding a example to `angular.equals` function.

Closes #14232
2016-04-07 15:17:54 +02:00
glenr4 85d7e09bef docs(ngAnimate): fix toggle button in example
The toggle button code on line 153 only sets bool to true, rather than toggling it.
The proposed change fixes this.

Closes #14387
2016-04-07 15:17:54 +02:00
andykuszyk 243a57648b docs(misc/Getting Started): fix markdown for headings
Closes #14353
2016-04-07 15:17:54 +02:00
Maciej Kołodziejczak 158dec330c docs(ngComponentRouter): fix a typo
Closes #14357
2016-04-07 15:17:53 +02:00
Andrew a1e63c8d4a docs(guide/Components): clarify output events with extra example and note
Add additional line from example which demonstrates using the snake cased attribute binding in
parent component template.
Add note clarifying camelCase to snake-case requirement to use the Output binding callback feature.

Closes #14365
2016-04-07 15:17:53 +02:00
Artur 8cc9978d15 docs(guide/Components): fix typo, improve style
Closes #14384
2016-04-07 15:17:52 +02:00
Robin Janssens 78a404da0e docs($httpBackend): update response data types
Updated docs to reflect that response data can either be an array, object _or_ a string
Technically, response data can be anything that can be handled by angular.copy,
but since string and JSON data is most commonly mocked, the main types are sufficient.

Closes #14346
2016-04-07 15:17:52 +02:00
Michał Gołębiowski f1bb8369b5 refactor(jshint): don't assume browser-only globals
(cherry-picked from ddad26402b)

Fixes #13442
Closes #14345
2016-04-06 20:43:06 +02:00
Peter Bacon Darwin 76b6941b4c docs(componentRouter): add custom installation instructions 2016-04-05 21:10:19 +01:00
Peter Bacon Darwin e72990dc37 fix($compile): don't throw if controller is named 2016-04-04 21:26:13 +01:00
Peter Bacon Darwin f338e96ccc feat($compile): put custom annotations on DDO
Closes #14369
Closes #14279
Closes #14284
2016-04-04 20:07:31 +01:00
Peter Bacon Darwin 0ad2b70862 fix($compile): ensure that $onChanges hook is called correctly
Due to the way that we instantiate controllers, the `$onChanges` hook
was not always available at the time we were trying to trigger the initial
call to this hook. For instance, if the hook was actually defined inside
the constructor function.

This commit fixes that but also fixes the fact that the initial call was
being made in the postDigest anyway, which was incorrect because the
it should have been made before the `$onInit` call.

Closes #14355
Closes #14359
2016-04-01 17:41:41 +01:00
Georgios Kalpakas 5261a374a9 test($browser): fix typo in property name (histroy --> history)
(The semantics of the test aren't affected, because we just needed a falsy value.)
2016-03-31 13:30:25 +03:00
Martin Staffa 832eba5fc9 fix(ngOptions): set select value when model matches disabled option
When ngModel is set to a value that matches a disabled option, ngOptions will now select the option
in the select element, which will set select.val() to the option hash value and visually
show the option value / label as selected in the select box. Previously, disabled
options forced the unknown value.
The previous behavior is inconsistent with both default HTML behavior and select with
ngModel but without ngOptions. Both allow disabled values to be selected programmatically.

A common use case for this behavior is an option that was previously valid, but has
been disabled, and cannot be selected again.

This commit removes a duplicate test, and all other tests that previously checked that disabled
options are not set have been adjusted to the ensure the opposite.

Fixes #12756
2016-03-30 11:42:07 +02:00
cscport 2b569fc0b2 docs(angular.bootstrap): fix capitalization in error message
Closes #14325
2016-03-27 15:43:37 +03:00
Lucas Mirelmann ee6aeb08bf docs(ngParseExt): Fix package name 2016-03-27 00:00:14 +01:00
Peter Bacon Darwin 8d43d8b8e7 feat($compile): add isFirstChange() method to onChanges object
Closes #14318
Closes #14323
2016-03-26 20:06:13 +00:00
Lucas Mirelmann d08f5c6986 feat(ngParseExt): New ngParseExt module
New ngParseExt module

Including this module into an application will extend $parse to allow identifiers
following ES6 identifiers
2016-03-26 20:41:11 +01:00
Lucas Mirelmann 3e7fa19197 feat($parse): Add the ability to define the identifier characters
Add the ability to define the identifier starts and identifier continue characters
2016-03-26 20:41:01 +01:00
Lucas Mirelmann 5d379db72b chore(bower): Add parse-ext repository 2016-03-26 20:40:49 +01:00
Peter Bacon Darwin 514639b585 docs(CHANGELOG): add 1.5.3 release notes 2016-03-25 20:01:45 +00:00
Peter Bacon Darwin 9cd9956dcb feat($compile): add more lifecycle hooks to directive controllers
This change adds in the following new lifecycle hooks, which map in some
way to those in Angular 2:

 * `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
   are the names of the bound properties that have changed, and the values are an object of the form
   `{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component such as
   cloning the bound value to prevent accidental mutation of the outer value.
 * `$onDestroy` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
   external resources, watches and event handlers.
 * `$postLink` - Called after this controller's element and its children been linked. Similar to the post-link
   function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
   Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
   they are waiting for their template to load asynchronously and their own compilation and linking has been
   suspended until that occurs.

Closes #14127
Closes #14030
Closes #14020
Closes #13991
Closes #14302
2016-03-25 12:56:08 +00:00
Martin Staffa c7813e9ebf fix(ngAnimate): run structural animations with cancelled out class changes
When multiple animations on the same element are queued before a $digest passes,
the animator tries to create as few actual animations as possible by joining / canceling
redundant animations. Class-based animations for example are cancelled when the classes that
are added and removed are the same, and the result is no class-change. This however must only
happen if there's no structural animation currently queued.

Fixes #14249
2016-03-24 00:13:16 +01:00
Martin Staffa ef91b04cdd fix(ngMessages): don't crash when nested messages are removed
Under specific circumstances, ngMessages would go into an infinite loop and crash the
browser / page:
- At least two ngMessage elements are wrapped inside another element (e.g. ngTransclude)
- The first message is currently visible
- The first message is removed (e.g. when the whole ngMessages element is removed by an ngIf)

When a message is removed, it looks for a previous message - in this specific case it would misidentify
the second message for a previous message, which would then cause the first message to be marked as the
second message's next message, resulting in an infinite loop, and crash.

This fix ensures that when searching for previous messages, ngMessage walks the DOM in a way so
that messages that come after the current message are never identified as previous messages.

This commit also detaches and destroys all child ngMessage elements when the ngMessages element is
destroyed, which should improve performance slightly.

Fixes #14183
Closes #14242
2016-03-24 00:13:16 +01:00
Alex Chuev 1acd97e18f docs(guide/component): add missing closing bracket
Closes #14299
2016-03-23 23:58:35 +02:00
Daniel Herman 513199ee9f fix($compile): workaround a GC bug in Chrome < 50
In the version of V8 used in Chrome < 50, the parent of template nodes for
`transclude: "element"` directives would be improperly garbage collected
despite still having been referenced via `parentNode`.

This bug surfaced due to the introduction of lazy transclusion (652b83e),
and appears under certain circumstances when using directive start and end elements.

It should be removed some time after Chrome 50 has been released.

Fixes #14041
Closes #14286
2016-03-23 22:06:17 +01:00
pmadruga 33f3c40e93 docs(error/$compile.baddir): mention "components" in directive name error
Closes #14212
2016-03-23 22:18:54 +02:00
Steve Mao 696cb95d5e docs($q): mention ES2015 (as a "synonym" for ES6) and remove "harmony"
Closes #14294
2016-03-22 12:09:02 +02:00
Georgios Kalpakas 457fd21a1a fix($sniffer): fix history sniffing in Chrome Packaged Apps
Although `window.history` is present in the context of Chrome Packaged Apps, it is not allowed to
access `window.history.pushState` or `window.history.state`, resulting in errors when trying to
"sniff" history support.
This commit fixes it by detecting a Chrome Packaged App (through the presence of
`window.chrome.app.runtime`). Note that `window.chrome.app` is present in the context of "normal"
webpages as well, but it doesn't have the `runtime` property, which is only available to packaged
apps (e.g. see https://developer.chrome.com/apps/api_index).

(It also also contains some style changes for making the structure and layout of `$sniffer` tests
 more consistent.)

Fixes #11932

Closes #13945
2016-03-22 11:57:18 +02:00
Wassim Chegham 3c6dfbf67d docs(guide/component-router): fix typos
Closes #14278
2016-03-22 02:18:44 +02:00
Owen Craig 3277b885c4 fix(formatNumber): handle small numbers correctly when gSize !== lgSize
By using `>=` when comparing the number length to `lgSize`, we'll provide the correct value, when
formatting numbers with different `lgSize` than `gSize`.

Fixes #14289

Closes #14290
2016-03-22 00:11:26 +02:00
Georgios Kalpakas 48a256d04b test(TzDate): fix test in Australia
Probably due to implementation differences in browsers for pre-DST period (see
https://github.com/angular/angular.js/issues/5017 and especially
https://github.com/angular/angular.js/issues/5017#issuecomment-90775226 for context), some
`TzDate` tests had different behavior on different Timezones/Regions (e.g. failed in Australia,
which started to observe DST in 1971).
Since the used year (`1970`) didn't have any particular significance, this commit fixes the issue
by using a year that is more consistently handled by browsers (`2000`).

Fixes #14272

Closes #14285
2016-03-21 20:45:35 +02:00
surya prakash singh 0579430799 docs(input[time]): fix a typo in the example
Closes #14220
2016-03-21 01:26:17 +02:00
Rongduan Zhu 39ac68dac1 docs(guide/component-router): changed path to match diagram
Closes #14277
2016-03-21 00:00:12 +02:00
Georgios Kalpakas 87fb44a5d3 docs(CHANGELOG.md): rearrange v1.5.1 to be right below v1.5.2
Moved the `v1.5.1` section above the `v1.4.10` one, so that it is right below the `v1.5.2` section
for easier reference. Also removed an empty "Breaking Changes" sub-section.

Closes #14283
2016-03-20 22:54:53 +02:00
Georgios Kalpakas 5c76b406f7 chore(ci-checks): fix the ddescribe-iit task for Jasmine 2
Closes #14276
2016-03-20 22:26:06 +02:00
Matias Niemelä f665968daf chore(CHANGELOG): update version reference 2016-03-18 15:37:43 -07:00
Matias Niemelä 5d1291c29d docs(CHANGELOG): add notes for v1.5.2 2016-03-18 15:04:06 -07:00
Martin Staffa ce7f400011 fix(ngAnimate.$animate): remove animation callbacks when the element is removed
The test for this didn't actually test the listener removal. The addClass animation after
the element removal didn't start because the enter animation was still in progress.
2016-03-18 17:10:26 +01:00
Martin Staffa 0fc8516b0c Revert "tests(jQuery): test on both oldest & latest supported jQuery version"
This reverts commit 94572e89c2. The commit snuck
in while cherry-picking commits from master.
2016-03-18 17:08:46 +01:00
Jason Bedard 28b2a9b583 style($compile,$controller): adding function names for debug/tracing
Closes #13420
2016-03-18 15:53:09 +01:00
Jason Bedard e8549372fc style($templateRequest): rename minError var to avoid name conflict
Closes #13701
2016-03-18 15:53:08 +01:00
Jason Bedard 5a434eb74e style(ngModel,ngOptions): make use of declared but unused variables 2016-03-18 15:53:06 +01:00
Jason Bedard 318de4db66 style(*): remove unused variables 2016-03-18 15:53:05 +01:00
Martin Staffa a6c79bf15d docs(guide/Services): add whitespace in code example
Closes #14156
2016-03-18 15:53:04 +01:00
Huc Arnaud c6a10a755e docs(error/tplrt): add missing ' in example code
Missing a ' @ line 46 class='wrapper'

Closes #14258
2016-03-18 15:53:03 +01:00
Maxim Salnikov 1d7bd5bf4f docs(guide/Component Router): fix typo in example code
Closes #14262
2016-03-18 15:53:03 +01:00
Michał Gołębiowski 94572e89c2 tests(jQuery): test on both oldest & latest supported jQuery version 2016-03-18 15:51:30 +01:00
Peter Bacon Darwin fee7bac392 revert: fix($compile): do not add <span> elements to root text nodes
This commit reverts 7617c08da6 which was accidentally
merged into 1.5.x (by @petebacondarwin in a moment of rebase madness) despite
it containing a breaking change.
2016-03-17 09:56:30 +00:00
Georgios Kalpakas dbb3b0f561 docs(CHANGELOG.md): add notes for v1.4.10 2016-03-16 21:17:02 +02:00
Michał Gołębiowski b8bfed6a52 tests(jQuery): make the tests pass on jQuery 3.0.0-beta1
Closes #14229
2016-03-16 18:29:13 +00:00
Georgios Kalpakas 7215a89e7d docs(CHANGELOG.md): fix typo in anchor name 2016-03-16 19:56:23 +02:00
Georgios Kalpakas b7cca56091 docs(CHANGELOG.md): add notes for v1.5.1 2016-03-16 19:53:43 +02:00
Josh Schneider 7338e433f8 docs(guide/component-router): fix incorrect hook name for $canActivate
The hook will most likely be named back to `$routerCanActivate` in the future,
but for now this change is accurate.

Closes #14237
2016-03-16 16:46:03 +00:00
Peter Bacon Darwin 119ed07d5a chore(travis): update node and browser versions 2016-03-16 14:08:10 +00:00
Peter Bacon Darwin 5f98ae8323 chore(package.json): fix up dist-tag
Now that 1.5.x is out of beta we need to change the dist-tag to `latest`
2016-03-16 14:08:10 +00:00
Peter Bacon Darwin 8a598b43bb chore(jenkins): update node version to 4.4 2016-03-16 14:08:10 +00:00
Peter Bacon Darwin e5dff4cfbe chore(jenkins): fix node version chooser in build scripts
The `set-node-version.sh` script was being run in its own shell and so
was not actually changing the current version of node.
2016-03-16 14:08:10 +00:00
Lucas Mirelmann 6c7a9cdd5f chore(*): Upgrade to Jasmine 2.4
Highlights:
New mechanism to run async tests as Jasmine 2 removed `runs`, `waits` and `waitsFor`
The functions `iit`, `ddescribe` and `tthey` were renamed `fit`, `fdescribe` and
`fthey` as the originals came from Karma, Karma no longer bundles Jasmine and the
new function name comes from Jasmine.

Closes #14226
2016-03-16 14:08:09 +00:00
Matias Niemelä 3b34f762fe chore(build): 1.5 versions should stick to 1.5.x 2016-03-16 14:06:49 +00:00
Martin Staffa fa167ba747 docs(guide/animations): fix code block styling 2016-03-16 14:06:49 +00:00
158 changed files with 11836 additions and 13782 deletions
+8 -3
View File
@@ -1,19 +1,24 @@
{
"bitwise": true,
"esversion": 6,
"immed": true,
"newcap": true,
"noarg": true,
"noempty": true,
"nonew": true,
"trailing": true,
"maxlen": 200,
"boss": true,
"eqnull": true,
"expr": true,
"globalstrict": true,
"laxbreak": true,
"loopfunc": true,
"strict": "global",
"sub": true,
"undef": true,
"indent": 2
"indent": 2,
"globals": {
"ArrayBuffer": false,
"Uint8Array": false
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
language: node_js
sudo: false
node_js:
- '4.2'
- '4.4'
cache:
directories:
+294
View File
@@ -1,3 +1,294 @@
<a name="1.5.4"></a>
# 1.5.4 graduated-sophistry (2016-04-14)
## Bug Fixes
- **$compile:**
- do not use `noop()` as controller for multiple components
([4c8aeefb](https://github.com/angular/angular.js/commit/4c8aeefb624de7436ad95f3cd525405e0c3f493e),
[#14391](https://github.com/angular/angular.js/issues/14391), [#14402](https://github.com/angular/angular.js/issues/14402))
- still trigger `$onChanges` even if the inner value already matches the new value
([d9448dcb](https://github.com/angular/angular.js/commit/d9448dcb9f901ceb04deda1d5f3d5aac8442a718),
[#14406](https://github.com/angular/angular.js/issues/14406))
- handle boolean attributes in `@` bindings
([499e1b2a](https://github.com/angular/angular.js/commit/499e1b2adf27f32d671123f8dceadb3df2ad84a9),
[#14070](https://github.com/angular/angular.js/issues/14070))
- don't throw if controller is named
([e72990dc](https://github.com/angular/angular.js/commit/e72990dc3714c8b847185ddb64fd5fd00e5cceab))
- ensure that `$onChanges` hook is called correctly
([0ad2b708](https://github.com/angular/angular.js/commit/0ad2b70862d49ecc4355a16d767c0ca9358ecc3e),
[#14355](https://github.com/angular/angular.js/issues/14355), [#14359](https://github.com/angular/angular.js/issues/14359))
- **$injector:** ensure functions with overridden `toString()` are annotated properly
([d384834f](https://github.com/angular/angular.js/commit/d384834fdee140a716298bd065f304f8fba4725e),
[#14361](https://github.com/angular/angular.js/issues/14361))
- **ngAnimate:**
- remove event listeners only after all listeners have been called
([79604f46](https://github.com/angular/angular.js/commit/79604f462899c118a99d610995083ff82d38aa35),
[#14321](https://github.com/angular/angular.js/issues/14321))
- fire callbacks when document is hidden
([c7a92d2a](https://github.com/angular/angular.js/commit/c7a92d2a9a436dddd65de721c9837a93e915d939),
[#14120](https://github.com/angular/angular.js/issues/14120))
- fire callbacks in the correct order for certain skipped animations
([90da3059](https://github.com/angular/angular.js/commit/90da3059cecfefaecf136b01cd87aee6775a8778))
- **ngClass:** fix watching of an array expression containing an object
([f975d8d4](https://github.com/angular/angular.js/commit/f975d8d4481e0b8cdba553f0e5ad9ec1688adae8),
[#14405](https://github.com/angular/angular.js/issues/14405))
- **ngMock:** fix collecting stack trace in `inject()` on IE10+, PhantomJS
([e9c718a4](https://github.com/angular/angular.js/commit/e9c718a465d28b9f2691e3acab944f7c31aa9fb6),
[#13591](https://github.com/angular/angular.js/issues/13591), [#13592](https://github.com/angular/angular.js/issues/13592), [#13593](https://github.com/angular/angular.js/issues/13593))
- **ngOptions:** set select value when model matches disabled option
([832eba5f](https://github.com/angular/angular.js/commit/832eba5fc952312e6b99127123e6e75bdf729006),
[#12756](https://github.com/angular/angular.js/issues/12756))
## Features
- **$compile:**
- put custom annotations on DDO
([f338e96c](https://github.com/angular/angular.js/commit/f338e96ccc739efc4b24022eae406c3d5451d422),
[#14369](https://github.com/angular/angular.js/issues/14369), [#14279](https://github.com/angular/angular.js/issues/14279), [#14284](https://github.com/angular/angular.js/issues/14284))
- add `isFirstChange()` method to onChanges object
([8d43d8b8](https://github.com/angular/angular.js/commit/8d43d8b8e7aacf97ddb9aa48bff25db57249cdd5),
[#14318](https://github.com/angular/angular.js/issues/14318), [#14323](https://github.com/angular/angular.js/issues/14323))
- **$componentController:** provide isolated scope if none is passed (#14425)
([33f817b9](https://github.com/angular/angular.js/commit/33f817b99cb20e566b381e7202235fe99b4a742a),
[#14425](https://github.com/angular/angular.js/issues/14425))
- **$http:**
- support handling additional XHR events
([01b18450](https://github.com/angular/angular.js/commit/01b18450882da9bb9c903d43c0daddbc03c2c35d) and
[56c861c9](https://github.com/angular/angular.js/commit/56c861c9e114c45790865e5635eaae8d32eb649a),
[#14367](https://github.com/angular/angular.js/issues/14367), [#11547](https://github.com/angular/angular.js/issues/11547), [#1934](https://github.com/angular/angular.js/issues/1934))
- **$parse:** add the ability to define the identifier characters
([3e7fa191](https://github.com/angular/angular.js/commit/3e7fa19197c54a764225ad27c0c0bf72263daa8d))
- **ngAnimate:** let $animate.off() remove all listeners for an element
([bf6cb8ab](https://github.com/angular/angular.js/commit/bf6cb8ab0d157083a1ed55743e3fffe728daa6f3))
- **ngAria:** add support for aria-readonly based on ngReadonly
([ec0baadc](https://github.com/angular/angular.js/commit/ec0baadcb68a4fa8da27d76b7e6a4e0840acd7fa),
[#14140](https://github.com/angular/angular.js/issues/14140), [#14077](https://github.com/angular/angular.js/issues/14077))
- **ngParseExt:** new ngParseExt module
([d08f5c69](https://github.com/angular/angular.js/commit/d08f5c698624f6243685b16f2d458cb9a980ebde))
## Performance Improvements
- **$compile:** use createMap() for directive bindings to allow fast `forEach`
([c115b37c](https://github.com/angular/angular.js/commit/c115b37c336f3a5936187279057b29c76078caf2),
[#12529](https://github.com/angular/angular.js/issues/12529))
- **ngOptions:** use `documentFragment` to populate `select` options
([6a4124d0](https://github.com/angular/angular.js/commit/6a4124d0fb17cd7fc0e8bf5a1ca4d785a1d11c1c),
[#13607](https://github.com/angular/angular.js/issues/13607), [#13239](https://github.com/angular/angular.js/issues/13239), [#12076](https://github.com/angular/angular.js/issues/12076))
<a name="1.5.3"></a>
# 1.5.3 diplohaplontic-meiosis (2016-03-25)
## Bug Fixes
- **$compile:** workaround a GC bug in Chrome < 50
([513199ee](https://github.com/angular/angular.js/commit/513199ee9f1c8eef1240983d6e52c824404adb98),
[#14041](https://github.com/angular/angular.js/issues/14041), [#14286](https://github.com/angular/angular.js/issues/14286))
- **$sniffer:** fix history sniffing in Chrome Packaged Apps
([457fd21a](https://github.com/angular/angular.js/commit/457fd21a1a0c10c66245c32a73602f3a09038bda),
[#11932](https://github.com/angular/angular.js/issues/11932), [#13945](https://github.com/angular/angular.js/issues/13945))
- **formatNumber:** handle small numbers correctly when `gSize` !== `lgSize`
([3277b885](https://github.com/angular/angular.js/commit/3277b885c4dec3edd51b8e8c3d1776057d6d4d1d),
[#14289](https://github.com/angular/angular.js/issues/14289), [#14290](https://github.com/angular/angular.js/issues/14290))
- **ngAnimate:** run structural animations with cancelled out class changes
([c7813e9e](https://github.com/angular/angular.js/commit/c7813e9ebf793fe89380dcad54e8e002fafdd985),
[#14249](https://github.com/angular/angular.js/issues/14249))
- **ngMessages:** don't crash when nested messages are removed
([ef91b04c](https://github.com/angular/angular.js/commit/ef91b04cdd794f308617bca7ebd0b1b747e4f7de),
[#14183](https://github.com/angular/angular.js/issues/14183), [#14242](https://github.com/angular/angular.js/issues/14242))
## Features
- **$compile:** add more lifecycle hooks to directive controllers
([9cd9956d](https://github.com/angular/angular.js/commit/9cd9956dcbc8382e8e8757a805398bd251bbc67e),
[#14127](https://github.com/angular/angular.js/issues/14127), [#14030](https://github.com/angular/angular.js/issues/14030), [#14020](https://github.com/angular/angular.js/issues/14020), [#13991](https://github.com/angular/angular.js/issues/13991), [#14302](https://github.com/angular/angular.js/issues/14302))
<a name="1.5.2"></a>
# 1.5.2 differential-recovery (2016-03-18)
This release reverts a breaking change that accidentally made it into the 1.5.1 release. See
[fee7bac3](https://github.com/angular/angular.js/commit/fee7bac392db24b6006d6a57ba71526f3afa102c)
for more info.
## Bug Fixes
- **ngAnimate.$animate:** remove animation callbacks when the element is removed
([ce7f4000](https://github.com/angular/angular.js/commit/ce7f400011e1e2e1b9316f18ce87b87b79d878b4))
<a name="1.5.1"></a>
# 1.5.1 equivocal-sophistication (2016-03-16)
## Bug Fixes
- **core:** only call `console.log` when `window.console` exists
([ce138f3c](https://github.com/angular/angular.js/commit/ce138f3c552f8bf741721ab8d10994ed35a4b2f5),
[#14006](https://github.com/angular/angular.js/issues/14006), [#14007](https://github.com/angular/angular.js/issues/14007), [#14047](https://github.com/angular/angular.js/issues/14047))
- **$compile:** allow directives to have decorators
([0728cc2f](https://github.com/angular/angular.js/commit/0728cc2f2bb04d5dbdfca41f3afacea16c75ee07))
- **$resource:** fix parse errors on older Android WebViews
([df8db7b4](https://github.com/angular/angular.js/commit/df8db7b446b5bae83afef457d706d2805e597f29),
[#13989](https://github.com/angular/angular.js/issues/13989))
- **$routeProvider:** properly handle optional eager path named groups
([c0797c68](https://github.com/angular/angular.js/commit/c0797c68866c9ef8ff3c2f6985e6eb9374346151),
[#14011](https://github.com/angular/angular.js/issues/14011))
- **copy:** add support for copying `Blob` objects
([e9d579b6](https://github.com/angular/angular.js/commit/e9d579b608c2be8fdcf0326d0679a76bb9ae5b6e),
[#9669](https://github.com/angular/angular.js/issues/9669), [#14064](https://github.com/angular/angular.js/issues/14064))
- **dateFilter:** correctly format BC years
([e36205f5](https://github.com/angular/angular.js/commit/e36205f5af82b69362def7d2b6eeeb038f592311))
- **formatNumber:** allow negative fraction size
([e046c170](https://github.com/angular/angular.js/commit/e046c170bcf677f26e61af6470cb5fd2f751c969),
[#13913](https://github.com/angular/angular.js/issues/13913))
- **input:** re-validate when partially editing date-family inputs
([e383804c](https://github.com/angular/angular.js/commit/e383804c4ab62278fbaf4fdfaa03caeacff77fc4),
[#12207](https://github.com/angular/angular.js/issues/12207), [#13886](https://github.com/angular/angular.js/issues/13886))
- **input\[date\]:** support years with more than 4 digits
([d76951f1](https://github.com/angular/angular.js/commit/d76951f1747abd2da6e320d4ff9019f170d9793f),
[#13735](https://github.com/angular/angular.js/issues/13735), [#13905](https://github.com/angular/angular.js/issues/13905))
- **ngOptions:** always set the 'selected' attribute for selected options
([9f5a1722](https://github.com/angular/angular.js/commit/9f5a172291ff6926dcd246f0972288916a4c9bf6),
[#14115](https://github.com/angular/angular.js/issues/14115))
- **ngRoute:** allow `ngView` to be included in an asynchronously loaded template
([8237482d](https://github.com/angular/angular.js/commit/8237482d49e76e2c4994fe6207e3c9799ef04163),
[#1213](https://github.com/angular/angular.js/issues/1213), [#6812](https://github.com/angular/angular.js/issues/6812), [#14088](https://github.com/angular/angular.js/issues/14088))
- **ngMock:**
- attach `$injector` to `$rootElement` and prevent memory leak due to attached data
([75373dd4](https://github.com/angular/angular.js/commit/75373dd4bdae6c6035272942c69444c386f824cd),
[#14022](https://github.com/angular/angular.js/issues/14022), [#14094](https://github.com/angular/angular.js/issues/14094), [#14098](https://github.com/angular/angular.js/issues/14098))
- don't break if `$rootScope.$destroy()` is not a function
([50ed8712](https://github.com/angular/angular.js/commit/50ed8712566d601c9fb76b71f7b534b5bc803a36),
[#14106](https://github.com/angular/angular.js/issues/14106), [#14107](https://github.com/angular/angular.js/issues/14107))
- **ngMockE2E:** pass `responseType` to `$delegate` when using `passThrough`
([d16faf9f](https://github.com/angular/angular.js/commit/d16faf9f2b9bd2b85d95e71d902cec0269282f2c),
[#5415](https://github.com/angular/angular.js/issues/5415), [#5783](https://github.com/angular/angular.js/issues/5783))
## Features
- **$compile:** add custom annotations to the controller
([0c800930](https://github.com/angular/angular.js/commit/0c8009300b819c39c5e4892856724a731a8dcda6),
[#14114](https://github.com/angular/angular.js/issues/14114))
- **$controllerProvider:** add a `has()` method for checking the existence of a controller
([bb9575db](https://github.com/angular/angular.js/commit/bb9575dbd3428176216355df7b2933d2a72783cd),
[#13951](https://github.com/angular/angular.js/issues/13951), [#14109](https://github.com/angular/angular.js/issues/14109))
- **dateFilter:** add support for STANDALONEMONTH in format (`LLLL`)
([3e5b25b3](https://github.com/angular/angular.js/commit/3e5b25b33f278376def432698c704b1807fdb8c0),
[#13999](https://github.com/angular/angular.js/issues/13999), [#14013](https://github.com/angular/angular.js/issues/14013))
- **ngMock:** add `sharedInjector()` to `angular.mock.module`
([a46ab60f](https://github.com/angular/angular.js/commit/a46ab60fd5bf94896f0761e858ef38b998eb0f80),
[#14093](https://github.com/angular/angular.js/issues/14093), [#10238](https://github.com/angular/angular.js/issues/10238))
## Performance Improvements
- **ngRepeat:** avoid duplicate jqLite wrappers
([632e15a3](https://github.com/angular/angular.js/commit/632e15a3afdcd30168700cec1367bd81966400d4))
- **ngAnimate:**
- avoid jqLite/jQuery for upward DOM traversal
([35251bd4](https://github.com/angular/angular.js/commit/35251bd4ce23251b5e9a2860cf414726c194721e))
- avoid `$.fn.data` overhead with jQuery
([15915e60](https://github.com/angular/angular.js/commit/15915e606fdf5114592db1a0a5e3f12e639d7cdb))
<a name="1.4.10"></a>
# 1.4.10 benignant-oscillation (2016-03-16)
## Bug Fixes
- **core:** only call `console.log` when `window.console` exists
([beb00e44](https://github.com/angular/angular.js/commit/beb00e44de947981dbe35d5cf7a116e10ea8dc67),
[#14006](https://github.com/angular/angular.js/issues/14006), [#14007](https://github.com/angular/angular.js/issues/14007), [#14047](https://github.com/angular/angular.js/issues/14047))
- **$animateCss:** cancel fallback timeout when animation ends normally
([a60bbc12](https://github.com/angular/angular.js/commit/a60bbc12e8c5170e70d95f1b2c3e309b3b95cb84),
[#13787](https://github.com/angular/angular.js/issues/13787))
- **$compile:**
- allow directives to have decorators
([77cdc37c](https://github.com/angular/angular.js/commit/77cdc37c65491b551fcf01a18ab848a693c293d7))
- properly denormalize templates when only one of the start/end symbols is different
([2d44a681](https://github.com/angular/angular.js/commit/2d44a681eb912a81a8bc8e16a278c45dae91fa24),
[#13848](https://github.com/angular/angular.js/issues/13848))
- handle boolean attributes in `@` bindings
([2ffbfb0a](https://github.com/angular/angular.js/commit/2ffbfb0ad0647d103ff339ee4b772b62d4823bf3),
[#13767](https://github.com/angular/angular.js/issues/13767), [#13769](https://github.com/angular/angular.js/issues/13769))
- **$parse:**
- prevent assignment on constructor properties
([f47e2180](https://github.com/angular/angular.js/commit/f47e218006029f39b4785d820b430de3a0eebcb0),
[#13417](https://github.com/angular/angular.js/issues/13417))
- preserve expensive checks when runnning `$eval` inside an expression
([96d62cc0](https://github.com/angular/angular.js/commit/96d62cc0fc77248d7e3ec4aa458bac0d3e072629))
- copy `inputs` for expressions with expensive checks
([0b7fff30](https://github.com/angular/angular.js/commit/0b7fff303f46202bbae1ff3ca9d0e5fa76e0fc9a))
- **$rootScope:** set no context when calling helper functions for `$watch`
([ab5c7698](https://github.com/angular/angular.js/commit/ab5c7698bb106669ca31b5f79a95afa54d65c5f1))
- **$route:** allow preventing a route reload
([4bc30314](https://github.com/angular/angular.js/commit/4bc3031497447ad527356f12bd0ceee1d7d09db5),
[#9824](https://github.com/angular/angular.js/issues/9824), [#13894](https://github.com/angular/angular.js/issues/13894))
- **$routeProvider:** properly handle optional eager path named groups
([6a4403a1](https://github.com/angular/angular.js/commit/6a4403a11845173d6a96232f77d73aa544b182af),
[#14011](https://github.com/angular/angular.js/issues/14011))
- **copy:** add support for copying `Blob` objects
([863a4232](https://github.com/angular/angular.js/commit/863a4232a6faa92428df45cd54d5a519be2434de),
[#9669](https://github.com/angular/angular.js/issues/9669), [#14064](https://github.com/angular/angular.js/issues/14064))
- **dateFilter:** follow the CLDR on pattern escape sequences
([f476060d](https://github.com/angular/angular.js/commit/f476060de6cc016380c0343490a184543f853652),
[#12839](https://github.com/angular/angular.js/issues/12839))
- **dateFilter, input:** fix Date parsing in IE/Edge when timezone offset contains `:`
([571afd65](https://github.com/angular/angular.js/commit/571afd6558786d7b99e2aebd307b4a94c9f2bb87),
[#13880](https://github.com/angular/angular.js/issues/13880), [#13887](https://github.com/angular/angular.js/issues/13887))
- **input:** re-validate when partially editing date-family inputs
([02929f82](https://github.com/angular/angular.js/commit/02929f82f30449301ff18fea84a6396a017683b1),
[#12207](https://github.com/angular/angular.js/issues/12207), [#13886](https://github.com/angular/angular.js/issues/13886))
- **select:** handle corner case of adding options via a custom directive
([df6e7315](https://github.com/angular/angular.js/commit/df6e731506831a3dc7f44c9a90abe17515450b3e),
[#13874](https://github.com/angular/angular.js/issues/13874), [#13878](https://github.com/angular/angular.js/issues/13878))
- **ngOptions:** always set the 'selected' attribute for selected options
([f87e8288](https://github.com/angular/angular.js/commit/f87e8288fb69526fd240a66a046f5de52ed204de),
[#14115](https://github.com/angular/angular.js/issues/14115))
- **ngAnimate:** properly cancel previously running class-based animations
([3b27dd37](https://github.com/angular/angular.js/commit/3b27dd37a2cc8a52992784ece6b371023dadf792),
[#10156](https://github.com/angular/angular.js/issues/10156), [#13822](https://github.com/angular/angular.js/issues/13822))
- **ngAnimateChildren:** make it compatible with `ngIf`
([dc158e7e](https://github.com/angular/angular.js/commit/dc158e7e40624ef94c66560386522ef7e991a9ce),
[#13865](https://github.com/angular/angular.js/issues/13865), [#13876](https://github.com/angular/angular.js/issues/13876))
- **ngMockE2E:** pass `responseType` to `$delegate` when using `passThrough`
([947cb4d1](https://github.com/angular/angular.js/commit/947cb4d1451afa4f5090a693df5b1968dd0df70c),
[#5415](https://github.com/angular/angular.js/issues/5415), [#5783](https://github.com/angular/angular.js/issues/5783))
## Features
- **$locale:** Include original locale ID in $locale
([e69f3550](https://github.com/angular/angular.js/commit/e69f35507e10c994708ce4f1efba7573951d1acd),
[#13390](https://github.com/angular/angular.js/issues/13390))
- **ngAnimate:** provide ng-[event]-prepare class for structural animations
([796f7ab4](https://github.com/angular/angular.js/commit/796f7ab41487e124b5b0c02dbf0a03bd581bf073))
## Performance Improvements
- **$compile:** avoid needless overhead when wrapping text nodes
([946d9ae9](https://github.com/angular/angular.js/commit/946d9ae90bb31fe911ebbe1b80cd4c8af5a665c6))
- **ngRepeat:** avoid duplicate jqLite wrappers
([d04c38c4](https://github.com/angular/angular.js/commit/d04c38c48968db777c3ea6a177ce2ff0116df7b4))
- **ngAnimate:**
- avoid jqLite/jQuery for upward DOM traversal
([ab95ba65](https://github.com/angular/angular.js/commit/ab95ba65c08b38cace83de6717b7681079182b45))
- avoid `$.fn.data` overhead with jQuery
([86416bcb](https://github.com/angular/angular.js/commit/86416bcbee2192fa31c017163c5d856763182ade))
<a name="1.5.0"></a>
# 1.5.0 ennoblement-facilitation (2016-02-05)
@@ -33,6 +324,9 @@
## Breaking Changes
### Upgrade to 1.5.1
This version of AngularJS is problematic due to a issue during its release. Please upgrade to version [1.5.2](#1.5.2).
- **ngAria:** due to [d06431e5](https://github.com/angular/angular.js/commit/d06431e5309bb0125588877451dc79b935808134),
Where appropriate, ngAria now applies ARIA to custom controls only, not native inputs. Because of this, support for `aria-multiline` on textareas has been removed.
+16 -3
View File
@@ -135,6 +135,9 @@ module.exports = function(grunt) {
ngMock: {
files: { src: 'src/ngMock/**/*.js' },
},
ngParseExt: {
files: { src: 'src/ngParseExt/**/*.js' },
},
ngResource: {
files: { src: 'src/ngResource/**/*.js' },
},
@@ -231,7 +234,11 @@ module.exports = function(grunt) {
dest: 'build/angular-aria.js',
src: util.wrap(files['angularModules']['ngAria'], 'module')
},
'promises-aplus-adapter': {
parseext: {
dest: 'build/angular-parse-ext.js',
src: util.wrap(files['angularModules']['ngParseExt'], 'module')
},
"promises-aplus-adapter": {
dest:'tmp/promises-aplus-adapter++.js',
src:['src/ng/q.js', 'lib/promises-aplus/promises-aplus-test-adapter.js']
}
@@ -249,7 +256,8 @@ module.exports = function(grunt) {
resource: 'build/angular-resource.js',
route: 'build/angular-route.js',
sanitize: 'build/angular-sanitize.js',
aria: 'build/angular-aria.js'
aria: 'build/angular-aria.js',
parseext: 'build/angular-parse-ext.js'
},
@@ -264,12 +272,17 @@ module.exports = function(grunt) {
],
options: {
disallowed: [
'fit',
'iit',
'xit',
'fthey',
'tthey',
'xthey',
'fdescribe',
'ddescribe',
'xdescribe'
'xdescribe',
'it.only',
'describe.only'
]
}
},
+18 -13
View File
@@ -8,20 +8,21 @@ synchronizes data from your UI (view) with your JavaScript objects (model) throu
binding. To help you structure your application better and make it easy to test, AngularJS teaches
the browser how to do dependency injection and inversion of control.
Oh yeah and it helps with server-side communication, taming async callbacks with promises and
deferreds. It also makes client-side navigation and deeplinking with hashbang urls or HTML5 pushState a
piece of cake. Best of all?? It makes development fun!
It also helps with server-side communication, taming async callbacks with promises and deferreds,
and it makes client-side navigation and deeplinking with hashbang urls or HTML5 pushState a
piece of cake. Best of all? It makes development fun!
* Web site: http://angularjs.org
* Tutorial: http://docs.angularjs.org/tutorial
* API Docs: http://docs.angularjs.org/api
* Developer Guide: http://docs.angularjs.org/guide
* Web site: https://angularjs.org
* Tutorial: https://docs.angularjs.org/tutorial
* API Docs: https://docs.angularjs.org/api
* Developer Guide: https://docs.angularjs.org/guide
* Contribution guidelines: [CONTRIBUTING.md](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md)
* Dashboard: http://dashboard.angularjs.org
* Dashboard: https://dashboard.angularjs.org
Building AngularJS
---------
[Once you have your environment set up](http://docs.angularjs.org/misc/contribute) just run:
[Once you have set up your environment](https://docs.angularjs.org/misc/contribute), just run:
grunt package
@@ -37,8 +38,12 @@ To execute end-to-end (e2e) tests, use:
grunt package
grunt test:e2e
To learn more about the grunt tasks, run `grunt --help` and also read our
[contribution guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md).
To learn more about the grunt tasks, run `grunt --help`
Contribute & Develop
--------------------
We've set up a separate document for our [contribution guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md).
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/README.md?pixel)](https://github.com/igrigorik/ga-beacon)
@@ -48,7 +53,7 @@ What to use AngularJS for and when to use it
AngularJS is the next generation framework where each component is designed to work with every other component in an interconnected way like a well-oiled machine. AngularJS is JavaScript MVC made easy and done right. (Well it is not really MVC, read on, to understand what this means.)
#### MVC, no, MV* done the right way!
MVC, short for Model-View-Controller, is a design pattern, i.e. how the code should be organized and how the different parts of an application separated for proper readability and debugging. Model is the data and the database. View is the user interface and what the user sees. Controller is the main link between Model and View. These are the three pillars of major programming frameworks present on the market today. On the other hand AngularJS works on MV*, short for Model-View-_Whatever_. The _Whatever_ is AngularJS's way of telling that you may create any kind of linking between the Model and the View here.
MVC, short for Model-View-Controller, is a design pattern, i.e. how the code should be organized and how the different parts of an application separated for proper readability and debugging. Model is the data and the database. View is the user interface and what the user sees. Controller is the main link between Model and View. These are the three pillars of major programming frameworks present on the market today. On the other hand AngularJS works on MV*, short for Model-View-_Whatever_. The _Whatever_ is AngularJS's way of telling that you may create any kind of linking between the Model and the View here.
Unlike other frameworks in any programming language, where MVC, the three separate components, each one has to be written and then connected by the programmer, AngularJS helps the programmer by asking him/her to just create these and everything else will be taken care of by AngularJS.
+5
View File
@@ -120,6 +120,10 @@ var angularFiles = {
'ngMessages': [
'src/ngMessages/messages.js'
],
'ngParseExt': [
'src/ngParseExt/ucd.js',
'src/ngParseExt/module.js'
],
'ngResource': [
'src/ngResource/resource.js'
],
@@ -205,6 +209,7 @@ var angularFiles = {
"karmaModules": [
'build/angular.js',
'@angularSrcModules',
'test/modules/no_bootstrap.js',
'src/ngScenario/browserTrigger.js',
'test/helpers/*.js',
'test/ngMessageFormat/*.js',
+4 -1
View File
@@ -19,9 +19,12 @@
"dump": false,
/* e2e */
"protractor": false,
"browser": false,
"element": false,
"by": false,
"$": false,
"$$": false,
/* testabilityPatch / matchers */
"inject": false,
@@ -39,4 +42,4 @@
"browserTrigger": false,
"jqLiteCacheSize": false
}
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ describe("DocsController", function() {
it("should update the Google Analytics with $location.path if currentPage is missing", inject(function($window, $location) {
$window._gaq = [];
spyOn($location, 'path').andReturn('x/y/z');
spyOn($location, 'path').and.returnValue('x/y/z');
$scope.$broadcast('$includeContentLoaded');
expect($window._gaq.pop()).toEqual(['_trackPageview', 'x/y/z']);
}));
+7 -7
View File
@@ -4,7 +4,7 @@ describe('errors', function() {
// Mock `ngSanitize` module
angular.
module('ngSanitize', []).
value('$sanitize', jasmine.createSpy('$sanitize').andCallFake(angular.identity));
value('$sanitize', jasmine.createSpy('$sanitize').and.callFake(angular.identity));
beforeEach(module('errors'));
@@ -103,12 +103,12 @@ describe('errors', function() {
it('should pass the final string through `$sanitize`', function() {
$sanitize.reset();
$sanitize.calls.reset();
var input = 'start https://foo/bar?baz#qux end';
var output = errorLinkFilter(input);
expect($sanitize.callCount).toBe(1);
expect($sanitize).toHaveBeenCalledTimes(1);
expect($sanitize).toHaveBeenCalledWith(output);
});
});
@@ -123,7 +123,7 @@ describe('errors', function() {
beforeEach(module(function($provide) {
$provide.decorator('errorLinkFilter', function() {
errorLinkFilter = jasmine.createSpy('errorLinkFilter');
errorLinkFilter.andCallFake(angular.identity);
errorLinkFilter.and.callFake(angular.identity);
return errorLinkFilter;
});
@@ -142,7 +142,7 @@ describe('errors', function() {
it('should interpolate the contents against `$location.search()`', function() {
spyOn($location, 'search').andReturn({p0: 'foo', p1: 'bar'});
spyOn($location, 'search').and.returnValue({p0: 'foo', p1: 'bar'});
var elem = $compile('<span error-display="foo = {0}, bar = {1}"></span>')($rootScope);
expect(elem.html()).toBe('foo = foo, bar = bar');
@@ -150,10 +150,10 @@ describe('errors', function() {
it('should pass the interpolated text through `errorLinkFilter`', function() {
$location.search = jasmine.createSpy('search').andReturn({p0: 'foo'});
$location.search = jasmine.createSpy('search').and.returnValue({p0: 'foo'});
var elem = $compile('<span error-display="foo = {0}"></span>')($rootScope);
expect(errorLinkFilter.callCount).toBe(1);
expect(errorLinkFilter).toHaveBeenCalledTimes(1);
expect(errorLinkFilter).toHaveBeenCalledWith('foo = foo', '_blank');
});
+1
View File
@@ -54,6 +54,7 @@ module.exports = new Package('angularjs', [
.config(function(parseTagsProcessor) {
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/tutorial-step'));
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/sortOrder'));
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/installation'));
})
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
name: 'installation'
};
@@ -0,0 +1,92 @@
{% extends "base.template.html" %}
{% block content %}
<h1>
{% if doc.title %}{$ doc.title | marked $}{% else %}{$ doc.name | code $}{% endif %}
</h1>
{% if doc.installation or doc.installation == '' %}
{$ doc.installation | marked $}
{% else %}
<h2>Installation</h2>
<p>First include {$ doc.packageFile | code $} in your HTML:</p>
{% code %}
<script src="angular.js">
<script src="{$ doc.packageFile $}">
{% endcode %}
<p>You can download this file from the following places:</p>
<ul>
<li>
<a href="https://developers.google.com/speed/libraries/devguide#angularjs">Google CDN</a><br>
e.g. {$ ("//ajax.googleapis.com/ajax/libs/angularjs/X.Y.Z/" + doc.packageFile) | code $}
</li>
<li>
<a href="http://bower.io">Bower</a><br>
e.g. {% code %}bower install {$ doc.packageName $}@X.Y.Z{% endcode %}
</li>
<li>
<a href="http://code.angularjs.org/">code.angularjs.org</a><br>
e.g. {% code %}"//code.angularjs.org/X.Y.Z/{$ doc.packageFile $}"{% endcode %}
</li>
</ul>
<p>where X.Y.Z is the AngularJS version you are running.</p>
<p>Then load the module in your application by adding it as a dependent module:</p>
{% code %}
angular.module('app', ['{$ doc.name $}']);
{% endcode %}
<p>With that you&apos;re ready to get started!</p>
{% endif %}
{$ doc.description | marked $}
{% if doc.knownIssueDocs %}
<div class="known-issues">
<h2 id="known-issues">Known Issues</h2>
<table class="definition-table">
<tr><th>Name</th><th>Description</th></tr>
{% for issueDoc in doc.knownIssueDocs -%}
<tr>
<td>{$ issueDoc.id | link(issueDoc.name, issueDoc) $}</td>
<td>
{% for issue in issueDoc.knownIssues -%}
{$ issue | marked $}
{% endfor -%}
</td>
</tr>
{% endfor -%}
</table>
</div>
{% endif %}
<div class="component-breakdown">
<h2>Module Components</h2>
{% for componentGroup in doc.componentGroups %}
<div>
<h3 class="component-heading" id="{$ componentGroup.groupType | dashCase $}">{$ componentGroup.groupType | title $}</h3>
<table class="definition-table">
<tr>
<th>Name</th>
<th>Description</th>
</tr>
{% for component in componentGroup.components %}
<tr>
<td>{$ component.id | link(component.name, component) $}</td>
<td>{$ component.description | firstParagraph | marked $}</td>
</tr>
{% endfor %}
</table>
</div>
{% endfor %}
</div>
{% if doc.usage %}
<h2>Usage</h2>
{$ doc.usage | marked $}
{% endif %}
{% endblock %}
+3 -3
View File
@@ -1,8 +1,8 @@
@ngdoc error
@name $compile:baddir
@fullName Invalid Directive Name
@fullName Invalid Directive/Component Name
@description
This error occurs when the name of a directive is not valid.
This error occurs when the name of a directive or component is not valid.
Directives must start with a lowercase character and must not contain leading or trailing whitespaces.
Directives and Components must start with a lowercase character and must not contain leading or trailing whitespaces.
+30
View File
@@ -0,0 +1,30 @@
@ngdoc error
@name $compile:infchng
@fullName Unstable `$onChanges` hooks
@description
This error occurs when the application's model becomes unstable because some `$onChanges` hooks are causing updates which then trigger
further calls to `$onChanges` that can never complete.
Angular detects this situation and prevents an infinite loop from causing the browser to become unresponsive.
For example, the situation can occur by setting up a `$onChanges()` hook which triggers an event on the component, which subsequently
triggers the component's bound inputs to be updated:
```html
<c1 prop="a" on-change="a = -a"></c1>
```
```js
function Controller1() {}
Controller1.$onChanges = function() {
this.onChange();
};
mod.component('c1', {
controller: Controller1,
bindings: {'prop': '<', onChange: '&'}
}
```
The maximum number of allowed iterations of the `$onChanges` hooks is controlled via TTL setting which can be configured via
{@link ng.$compileProvider#onChangesTtl `$compileProvider.onChangesTtl`}.
+1 -1
View File
@@ -43,7 +43,7 @@ well. Consider the following template:
```
<div class='container'>
<div class='wrapper>
<div class='wrapper'>
...
</div> <!-- wrapper -->
</div> <!-- container -->
+90 -69
View File
@@ -10,7 +10,7 @@ The goal of ngAria is to improve Angular's default accessibility by enabling com
[ARIA](http://www.w3.org/TR/wai-aria/) attributes that convey state or semantic information for
assistive technologies used by persons with disabilities.
##Including ngAria
## Including ngAria
Using {@link ngAria ngAria} is as simple as requiring the ngAria module in your application. ngAria hooks into
standard AngularJS directives and quietly injects accessibility support into your application
@@ -20,7 +20,7 @@ at runtime.
angular.module('myApp', ['ngAria'])...
```
###Using ngAria
### Using ngAria
Most of what ngAria does is only visible "under the hood". To see the module in action, once you've
added it as a dependency, you can test a few things:
* Using your favorite element inspector, look for attributes added by ngAria in your own code.
@@ -28,12 +28,13 @@ added it as a dependency, you can test a few things:
* Fire up a screen reader such as VoiceOver or NVDA to check for ARIA support.
[Helpful screen reader tips.](http://webaim.org/articles/screenreader_testing/)
##Supported directives
## Supported directives
Currently, ngAria interfaces with the following directives:
* {@link guide/accessibility#ngmodel ngModel}
* {@link guide/accessibility#ngdisabled ngDisabled}
* {@link guide/accessibility#ngrequired ngRequired}
* {@link guide/accessibility#ngreadonly ngReadonly}
* {@link guide/accessibility#ngvaluechecked ngChecked}
* {@link guide/accessibility#ngvaluechecked ngValue}
* {@link guide/accessibility#ngshow ngShow}
@@ -57,12 +58,62 @@ attributes (if they have not been explicitly specified by the developer):
* aria-valuenow
* aria-invalid
* aria-required
* aria-readonly
###Example
### Example
<example module="ngAria_ngModelExample" deps="angular-aria.js">
<file name="index.html">
<style>
<file name="index.html">
<form ng-controller="formsController">
<some-checkbox role="checkbox" ng-model="checked" ng-class="{active: checked}"
ng-disabled="isDisabled" ng-click="toggleCheckbox()"
aria-label="Custom Checkbox" show-attrs>
<span class="icon" aria-hidden="true"></span>
Custom Checkbox
</some-checkbox>
</form>
</file>
<file name="script.js">
var app = angular.module('ngAria_ngModelExample', ['ngAria'])
.controller('formsController', function($scope){
$scope.checked = false;
$scope.toggleCheckbox = function(){
$scope.checked = !$scope.checked;
};
})
.directive('someCheckbox', function(){
return {
restrict: 'E',
link: function($scope, $el, $attrs) {
$el.on('keypress', function(event){
event.preventDefault();
if(event.keyCode === 32 || event.keyCode === 13){
$scope.toggleCheckbox();
$scope.$apply();
}
});
}
};
})
.directive('showAttrs', function() {
return function($scope, $el, $attrs) {
var pre = document.createElement('pre');
$el.after(pre);
$scope.$watch(function() {
var $attrs = {};
Array.prototype.slice.call($el[0].attributes, 0).forEach(function(item) {
if (item.name !== 'show-$attrs') {
$attrs[item.name] = item.value;
}
});
return $attrs;
}, function(newAttrs, oldAttrs) {
pre.textContent = JSON.stringify(newAttrs, null, 2);
}, true);
};
});
</file>
<file name="style.css">
[role=checkbox] {
cursor: pointer;
display: inline-block;
@@ -81,58 +132,7 @@ attributes (if they have not been explicitly specified by the developer):
pre {
white-space: pre-wrap;
}
</style>
<div>
<form ng-controller="formsController">
<some-checkbox role="checkbox" ng-model="checked" ng-class="{active: checked}"
ng-disabled="isDisabled" ng-click="toggleCheckbox()"
aria-label="Custom Checkbox" show-attrs>
<span class="icon" aria-hidden="true"></span>
Custom Checkbox
</some-checkbox>
</form>
</div>
<script>
var app = angular.module('ngAria_ngModelExample', ['ngAria'])
.controller('formsController', function($scope){
$scope.checked = false;
$scope.toggleCheckbox = function(){
$scope.checked = !$scope.checked;
}
})
.directive('someCheckbox', function(){
return {
restrict: 'E',
link: function($scope, $el, $attrs) {
$el.on('keypress', function(event){
event.preventDefault();
if(event.keyCode === 32 || event.keyCode === 13){
$scope.toggleCheckbox();
$scope.$apply();
}
});
}
}
})
.directive('showAttrs', function() {
return function($scope, $el, $attrs) {
var pre = document.createElement('pre');
$el.after(pre);
$scope.$watch(function() {
var $attrs = {};
Array.prototype.slice.call($el[0].attributes, 0).forEach(function(item) {
if (item.name !== 'show-$attrs') {
$attrs[item.name] = item.value;
}
});
return $attrs;
}, function(newAttrs, oldAttrs) {
pre.textContent = JSON.stringify(newAttrs, null, 2);
}, true);
}
});
</script>
</file>
</file>
</example>
ngAria will also add `tabIndex`, ensuring custom elements with these roles will be reachable from
@@ -147,7 +147,7 @@ To ease the transition between native inputs and custom controls, ngAria now sup
The original directives were created for native inputs only, so ngAria extends
support to custom elements by managing `aria-checked` for accessibility.
###Example
### Example
```html
<custom-checkbox ng-checked="val"></custom-checkbox>
@@ -169,7 +169,7 @@ using ngAria with {@link ng.ngDisabled ngDisabled} will also
add `aria-disabled`. This tells assistive technologies when a non-native input is disabled, helping
custom controls to be more accessible.
###Example
### Example
```html
<md-checkbox ng-disabled="disabled"></md-checkbox>
@@ -181,8 +181,10 @@ Becomes:
<md-checkbox disabled aria-disabled="true"></md-checkbox>
```
>You can check whether a control is legitimately disabled for a screen reader by visiting
<div class="alert alert-info">
You can check whether a control is legitimately disabled for a screen reader by visiting
[chrome://accessibility](chrome://accessibility) and inspecting [the accessibility tree](http://www.paciellogroup.com/blog/2015/01/the-browser-accessibility-tree/).
</div>
<h2 id="ngrequired">ngRequired</h2>
@@ -191,7 +193,7 @@ The boolean `required` attribute is only valid for native form controls such as
as required, using ngAria with {@link ng.ngRequired ngRequired} will also add
`aria-required`. This tells accessibility APIs when a custom control is required.
###Example
### Example
```html
<md-checkbox ng-required="val"></md-checkbox>
@@ -203,9 +205,28 @@ Becomes:
<md-checkbox ng-required="val" aria-required="true"></md-checkbox>
```
<h2 id="ngreadonly">ngReadonly</h2>
The boolean `readonly` attribute is only valid for native form controls such as `input` and
`textarea`. To properly indicate custom element directives such as `<md-checkbox>` or `<custom-input>`
as required, using ngAria with {@link ng.ngReadonly ngReadonly} will also add
`aria-readonly`. This tells accessibility APIs when a custom control is read-only.
### Example
```html
<md-checkbox ng-readonly="val"></md-checkbox>
```
Becomes:
```html
<md-checkbox ng-readonly="val" aria-readonly="true"></md-checkbox>
```
<h2 id="ngshow">ngShow</h2>
>The {@link ng.ngShow ngShow} directive shows or hides the
The {@link ng.ngShow ngShow} directive shows or hides the
given HTML element based on the expression provided to the `ngShow` attribute. The element is
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
@@ -222,7 +243,7 @@ screen reader users won't accidentally focus on "mystery elements". Managing tab
child control can be complex and affect performance, so its best to just stick with the default
`display: none` CSS. See the [fourth rule of ARIA use](http://www.w3.org/TR/aria-in-html/#fourth-rule-of-aria-use).
###Example
### Example
```css
.ng-hide {
display: block;
@@ -242,7 +263,7 @@ Becomes:
<h2 id="nghide">ngHide</h2>
>The {@link ng.ngHide ngHide} directive shows or hides the
The {@link ng.ngHide ngHide} directive shows or hides the
given HTML element based on the expression provided to the `ngHide` attribute. The element is
shown or hidden by removing or adding the `.ng-hide` CSS class onto the element.
@@ -283,11 +304,11 @@ Becomes:
<h2 id="ngmessages">ngMessages</h2>
The new ngMessages module makes it easy to display form validation or other messages with priority
The ngMessages module makes it easy to display form validation or other messages with priority
sequencing and animation. To expose these visual messages to screen readers,
ngAria injects `aria-live="assertive"`, causing them to be read aloud any time a message is shown,
regardless of the user's focus location.
###Example
### Example
```html
<div ng-messages="myForm.myName.$error">
@@ -305,7 +326,7 @@ Becomes:
</div>
```
##Disabling attributes
## Disabling attributes
The attribute magic of ngAria may not work for every scenario. To disable individual attributes,
you can use the {@link ngAria.$ariaProvider#config config} method. Just keep in mind this will
tell ngAria to ignore the attribute globally.
@@ -343,7 +364,7 @@ tell ngAria to ignore the attribute globally.
</file>
</example>
##Common Accessibility Patterns
## Common Accessibility Patterns
Accessibility best practices that apply to web apps in general also apply to Angular.
+2
View File
@@ -430,10 +430,12 @@ You can prevent this unwanted behavior by adding CSS to the `.ng-animate` class
for the whole duration of an animation. Simply overwrite the transition / animation duration. In the
case of the spinner, this would be:
```css
.spinner.ng-animate {
transition: 0s none;
animation: 0s none;
}
```
If you do have CSS transitions / animations defined for the animation events, make sure they have higher priority
than any styles that are independent from ngAnimate.
+15 -15
View File
@@ -33,7 +33,7 @@ Here is a table of the main concepts used in the Component Router.
## Component-based Applications
It recommended to develop AngularJS applications as a hierarchy of Components. Each Component
It is recommended to develop AngularJS applications as a hierarchy of Components. Each Component
is an isolated part of the application, which is responsible for its own user interface and has
a well defined programmatic interface to the Component that contains it. Take a look at the
{@link guide/component component guide} for more information.
@@ -105,7 +105,7 @@ Here we have specified that the **Root Component** is the component directive wi
Remember to instantiate this **Root Component** in our `index.html` file.
```html
<my-app><my-app>
<my-app></my-app>
```
## Route Matching
@@ -124,9 +124,9 @@ This process continues until we run out of **Routing Components** or consume the
![Routed Components](img/guide/component-routes.svg)
In the previous diagram can see that the URL `/heros/2` has been matched against the `App`, `Heroes` and
In the previous diagram, we can see that the URL `/heros/4` has been matched against the `App`, `Heroes` and
`HeroDetail` **Routing Components**. The **Routers** for each of the **Routing Components** consumed a part
of the URL: "/", "/heroes" and "/2" respectively.
of the URL: "/", "/heroes" and "/4" respectively.
The result is that we end up with a hierarchy of **Routing Components** rendered in **Outlets**, via the
{@link ngOutlet} directive, in each **Routing Component's** template, as you can see in the following diagram.
@@ -462,7 +462,7 @@ to display list and detail views of Heroes and Crises.
## Install the libraries
It is simplest to use npm to install the **Component Router** module. For this guide we will also install
It is easier to use npm to install the **Component Router** module. For this guide we will also install
AngularJS itself via npm:
```bash
@@ -485,7 +485,7 @@ Just like any Angular application, we load the JavaScript files into our `index.
## Create the `app` module
In the app.js file, create the main application module `app` which depends upon the `ngComponentRouter`
In the app.js file, create the main application module `app` which depends on the `ngComponentRouter`
module, which is provided by the **Component Router** script.
```js
@@ -494,10 +494,10 @@ angular.module('app', ['ngComponentRouter'])
We must choose what **Location Mode** the **Router** should use. We are going to use HTML5 mode locations,
so that we will not have hash-based paths. We must rely on the browser to provide `pushState` support,
which is true of most modern browsers. See {@link $locationProvider#html5Mode} for more information.
which is true for most modern browsers. See {@link $locationProvider#html5Mode} for more information.
<div class="alert alert-info">
Using HTML5 mode means that we can have clean URLs for our application routes but it does require that our
Using HTML5 mode means that we can have clean URLs for our application routes. However, HTML5 mode does require that our
web server, which hosts the application, understands that it must respond with the index.html file for
requests to URLs that represent all our application routes. We are going to use the `lite-server` web server
to do this for us.
@@ -550,7 +550,7 @@ Bootstrap the Angular application and add the top level App Component.
# Implementing the AppComponent
In the previous section we created a single top level **App Component**. Let's now create some more
In the previous section we have created a single top level **App Component**. Let's now create some more
**Routing Components** and wire up **Route Config** for those. We start with a Heroes Feature, which
will display one of two views.
@@ -590,7 +590,7 @@ of this view will be rendered.
### ngLink
We have used the `ng-link` directive to create a link to navigate to the Heroes Component. By using this
directive we don't need to know what the actual URL will be. We can leave the Router to generate that for us.
directive we don't need to know what the actual URL will be. We can let the Router generate that for us.
We have included a link to the Crisis Center but have not included the `ng-link` directive as we have not yet
implemented the CrisisCenter component.
@@ -765,7 +765,7 @@ function HeroListComponent(heroService) {
Running the application should update the browser's location to `/heroes` and display the list of heroes
returned from the `heroService`.
By returning a promise for the list of heroes from `$routerOnActivate()` we can delay activation of the
By returning a promise for the list of heroes from `$routerOnActivate()` we can delay the activation of the
Route until the heroes have arrived successfully. This is similar to how a `resolve` works in {@link ngRoute}.
@@ -956,9 +956,9 @@ respectively.
**How do I prevent navigation from occurring?**
Each **Component** can provide the `$routerCanActivate` and `$routerCanDeactivate` **Lifecycle Hooks**. The
`$routerCanDeactivate` hook is an instance method on the **Component**. The `$routerCanActivate` hook is a
static method defined on either the **Component Definition Object** or the **Component's** constructor function.
Each **Component** can provide the `$canActivate` and `$routerCanDeactivate` **Lifecycle Hooks**. The
`$routerCanDeactivate` hook is an instance method on the **Component**. The `$canActivate` hook is used as a
static method defined on the **Component Definition Object**.
The **Router** will call these hooks to control navigation from one **Route** to another. Each of these hooks can
return a `boolean` or a Promise that will resolve to a `boolean`.
@@ -966,7 +966,7 @@ return a `boolean` or a Promise that will resolve to a `boolean`.
During a navigation, some **Components** will become inactive and some will become active. Before the navigation
can complete, all the **Components** must agree that they can be deactivated or activated, respectively.
The **Router** will call the `$routerCanDeactivate` and `$routerCanActivate` hooks, if they are provided. If any
The **Router** will call the `$routerCanDeactivate` and `$canActivate` hooks, if they are provided. If any
of the hooks resolve to `false` then the navigation is cancelled.
### Dialog Box Service
+27 -1
View File
@@ -29,7 +29,7 @@ and link functions are unavailable
Components can be registered using the `.component()` method of an Angular module (returned by {@link module `angular.module()`}). The method takes two arguments:
* The name of the Component (as string).
* The Component config object (note that, unlike the `.directive()` method, this method does **not** take a factory function.
* The Component config object. (Note that, unlike the `.directive()` method, this method does **not** take a factory function.)
<example name="heroComponentSimple" module="heroApp">
<file name="index.js">
@@ -133,6 +133,8 @@ components should follow a few simple conventions:
For a deletion, that means the component doesn't delete the `hero` itself, but sends it back to
the owner component via the correct event.
```html
<!-- note that we use snake case for bindings in the template as usual -->
<editable-field on-update="$ctrl.update('location', value)"></editable-field><br>
<button ng-click="$ctrl.onDelete({hero: $ctrl.hero})">Delete</button>
```
- That way, the parent component can decide what to do with the event (e.g. delete an item or update the properties)
@@ -147,6 +149,30 @@ components should follow a few simple conventions:
}
```
- **Components have a well-defined lifecycle**
Each component can implement "lifecycle hooks". These are methods that will be called at certain points in the life
of the component. The following hook methods can be implemented:
* `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
had their bindings initialized (and before the pre &amp; post linking functions for the directives on
this element). This is a good place to put initialization code for your controller.
* `$onChanges(changesObj)` - Called whenever one-way bindings are updated. The `changesObj` is a hash whose keys
are the names of the bound properties that have changed, and the values are an object of the form
`{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a component such as
cloning the bound value to prevent accidental mutation of the outer value.
* `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
external resources, watches and event handlers.
* `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
they are waiting for their template to load asynchronously and their own compilation and linking has been
suspended until that occurs.
This hook can be considered analogous to the `ngAfterViewInit` and `ngAfterContentInit` hooks in Angular 2.
Since the compilation process is rather different in Angular 1 there is no direct mapping and care should
be taken when upgrading.
By implementing these methods, your component can hook into its lifecycle.
- **An application is a tree of components:**
Ideally, the whole application should be a tree of components that implement clearly defined inputs
and outputs, and minimize two-way data binding. That way, it's easier to predict when data changes and what the state
+1 -1
View File
@@ -257,7 +257,7 @@ the `$digest` phase. This delay is desirable, since it coalesces multiple model
2. **Watcher registration**
During template linking directives register {@link
During template linking, directives register {@link
ng.$rootScope.Scope#$watch watches} on the scope. These watches will be
used to propagate model values to the DOM.
+1 -1
View File
@@ -43,7 +43,7 @@ subsystem takes care of the rest.
<file name="script.js">
angular.
module('myServiceModule', []).
controller('MyController', ['$scope','notify', function ($scope, notify) {
controller('MyController', ['$scope', 'notify', function ($scope, notify) {
$scope.callNotify = function(msg) {
notify(msg);
};
+4 -4
View File
@@ -16,9 +16,9 @@ becoming an Angular expert.
starter app with a directory layout, test harness, and scripts to begin building your application.
#Further Steps
# Further Steps
##Watch Videos
## Watch Videos
If you havent had a chance to watch the videos from the homepage, please check out:
@@ -29,13 +29,13 @@ If you havent had a chance to watch the videos from the homepage, please chec
And visit our [YouTube channel](http://www.youtube.com/user/angularjs) for more AngularJS video presentations and
tutorials.
##Subscribe
## Subscribe
* Subscribe to the [mailing list](http://groups.google.com/forum/?fromgroups#!forum/angular). Ask questions here!
* Follow us on [Twitter](https://twitter.com/intent/follow?original_referer=http%3A%2F%2Fangularjs.org%2F&region=follow_link&screen_name=angularjs&source=followbutton&variant=2.0)
* Add us to your circles on [Google+](https://plus.google.com/110323587230527980117/posts)
##Read more
## Read more
The AngularJS documentation includes the {@link guide/index Developer Guide} covering concepts and the
{@link ./api API Reference} for syntax and usage.
+44 -5
View File
@@ -1,5 +1,9 @@
"use strict";
var fs = require('fs');
var _ = require('lodash');
var stripJsonComments = require('strip-json-comments');
var gulp = require('gulp');
var log = require('gulp-util').log;
var concat = require('gulp-concat');
@@ -25,6 +29,23 @@ var ignoredFiles = '!src/angular.bind.js';
var assets = 'app/assets/**/*';
var getJshintConfig = function(filepath) {
return JSON.parse(stripJsonComments(fs.readFileSync(filepath, {encoding: 'utf-8'})));
};
var getMergedJshintConfig = function(filepath) {
// "extends" doesn't work in configuration passed by an object, we need to do the extending ourselves.
var config = getJshintConfig(filepath);
var baseConfig = getJshintConfig('../.jshintrc-base');
_.merge(config, baseConfig);
delete config.extends;
// Examples don't run in strict mode; accept that for now.
config.strict = false;
return config;
};
var copyComponent = function(component, pattern, sourceFolder, packageFile) {
pattern = pattern || '/**/*';
sourceFolder = sourceFolder || bowerFolder;
@@ -90,17 +111,35 @@ gulp.task('assets', ['bower'], function() {
gulp.task('doc-gen', ['bower'], function() {
var dgeni = new Dgeni([require('./config')]);
return dgeni.generate().catch(function(error) {
return dgeni.generate().catch(function() {
process.exit(1);
});
});
// JSHint the example and protractor test files
gulp.task('jshint', ['doc-gen'], function() {
gulp.src([outputFolder + '/ptore2e/**/*.js', outputFolder + '/examples/**/*.js'])
.pipe(jshint())
.pipe(jshint.reporter('jshint-stylish'))
.pipe(jshint.reporter('fail'));
var examplesConfig = getMergedJshintConfig('../docs/app/test/.jshintrc');
// Some tests use `alert` which is not assumed to be available even with `"browser": true`.
examplesConfig.globals.alert = false;
var protractorConfig = getMergedJshintConfig('../docs/app/e2e/.jshintrc');
return merge(
gulp.src([
outputFolder + '/examples/**/*.js',
'!' + outputFolder + '/examples/**/protractor.js',
])
.pipe(jshint(examplesConfig))
.pipe(jshint.reporter('jshint-stylish'))
.pipe(jshint.reporter('fail')),
gulp.src([
outputFolder + '/ptore2e/**/*.js',
outputFolder + '/examples/**/protractor.js',
])
.pipe(jshint(protractorConfig))
.pipe(jshint.reporter('jshint-stylish'))
.pipe(jshint.reporter('fail'))
);
});
+4
View File
@@ -9,4 +9,8 @@ npm run test-i18n
node src/closureSlurper.js
npm run test-i18n-ucd
echo "Generating ngParseExt"
node ucd/src/extract.js
+53
View File
@@ -0,0 +1,53 @@
var extractValues = require('../src/extractValues.js').extractValues;
var stream = require('stream');
function StringStream(str) {
return new stream.Readable({
read: function(n) {
this.push(str);
str = null;
}
});
}
describe('extractValues', function() {
it('should extract the values from the xml', function(done) {
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><char cp="0002" IDS="Y"></char><char cp="0003" IDS="N"></char></repertoire></ucd>';
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
expect(values).toEqual({ IDS_Y : [ [ '0001', '0002' ] ] });
done();
});
});
it('should extract the values from the xml if the last element matches', function(done) {
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><char cp="0002" IDS="Y"></char><char cp="0003" IDS="Y"></char></repertoire></ucd>';
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
expect(values).toEqual({ IDS_Y : [ [ '0001', '0003' ] ] });
done();
});
});
it('should support `reserved`', function(done) {
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><reserved first-cp="0002" last-cp="0005" IDS="N"></reserved><char cp="0006" IDS="Y"></char></repertoire></ucd>';
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
expect(values).toEqual({ IDS_Y : [ [ '0001', '0001' ], [ '0006', '0006' ] ] });
done();
});
});
it('should support `surrogate`', function(done) {
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><surrogate first-cp="0002" last-cp="0005" IDS="N"></surrogate><char cp="0006" IDS="Y"></char></repertoire></ucd>';
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
expect(values).toEqual({ IDS_Y : [ [ '0001', '0001' ], [ '0006', '0006' ] ] });
done();
});
});
it('should support `noncharactere`', function(done) {
var str = '<ucd><repertoire><char cp="0000" IDS="N"></char><char cp="0001" IDS="Y"></char><noncharacter first-cp="0002" last-cp="0005" IDS="N"></noncharacter><char cp="0006" IDS="Y"></char></repertoire></ucd>';
extractValues(StringStream(str), {'IDS': 'Y'}, function(values) {
expect(values).toEqual({ IDS_Y : [ [ '0001', '0001' ], [ '0006', '0006' ] ] });
done();
});
});
});
+54
View File
@@ -0,0 +1,54 @@
var generateCodeModule = require('../src/generateCode.js');
var generateCode = generateCodeModule.generateCode;
var generateFunction = generateCodeModule.generateFunction;
describe('generateFunction', function() {
it('should generate function with ranges', function() {
expect(generateFunction([ [ '0001', '0003' ] ], 'IDS_Y')).toEqual('\
function IDS_Y(cp) {\n\
if (0x0001 <= cp && cp <= 0x0003) return true;\n\
return false;\n\
}\n');
});
it('should generate function with multiple ranges', function() {
expect(generateFunction([ [ '0001', '0003' ], [ '0005', '0009'] ], 'IDS_Y')).toEqual('\
function IDS_Y(cp) {\n\
if (0x0001 <= cp && cp <= 0x0003) return true;\n\
if (0x0005 <= cp && cp <= 0x0009) return true;\n\
return false;\n\
}\n');
});
it('should generate function with unique values', function() {
expect(generateFunction([ [ '0001', '0001' ], [ '0005', '0009'] ], 'IDS_Y')).toEqual('\
function IDS_Y(cp) {\n\
if (cp === 0x0001) return true;\n\
if (0x0005 <= cp && cp <= 0x0009) return true;\n\
return false;\n\
}\n');
});
});
describe('generateCode', function() {
it('should generate the function for all the values', function() {
expect(generateCode({ IDS_Y : [ [ '0001', '0001' ], [ '0006', '0006' ] ], IDC_Y : [ [ '0002', '0002' ], [ '0007', '0007' ] ] })).toEqual('\
/******************************************************\n\
* Generated file, do not modify *\n\
* *\n\
*****************************************************/\n\
"use strict";\n\
function IDS_Y(cp) {\n\
if (cp === 0x0001) return true;\n\
if (cp === 0x0006) return true;\n\
return false;\n\
}\n\
function IDC_Y(cp) {\n\
if (cp === 0x0002) return true;\n\
if (cp === 0x0007) return true;\n\
return false;\n\
}\n\
');
});
});
+28
View File
@@ -0,0 +1,28 @@
"use strict";
var fs = require('fs');
var zlib = require('zlib');
var extractValues = require('./extractValues').extractValues;
var generateCode = require('./generateCode').generateCode;
var generateextractValues = require('./extractValues').extractValues;
// ID_Start and ID_Continue
var propertiesToExtract = {'IDS': 'Y', 'IDC': 'Y'};
function main() {
extractValues(
fs.createReadStream('./ucd/src/ucd.all.flat.xml.gz').pipe(zlib.createGunzip()),
propertiesToExtract,
writeFile);
function writeFile(validRanges) {
var code = generateCode(validRanges);
try {
var stats = fs.lstatSync('../src/ngParseExt');
} catch (e) {
fs.mkdirSync('../src/ngParseExt');
}
fs.writeFile('../src/ngParseExt/ucd.js', code);
}
}
main();
+58
View File
@@ -0,0 +1,58 @@
/**
* Extract values from a stream.
*/
exports.extractValues = extractValues;
var sax = require('sax/lib/sax');
var saxStrict = true;
var saxOptions = {};
var validXMLTagNames = { char: 'Y', reserved: 'Y', surrogate: 'Y', noncharacter: 'Y'};
function extractValues(stream, propertiesToExtract, callback) {
var saxStream = sax.createStream(saxStrict, saxOptions);
var firstValid = {};
var lastValid = {};
var keys = Object.keys(propertiesToExtract);
var keyValues = keys.map(function(k) { return propertiesToExtract[k]; });
var validRanges = {};
for (var i in keys) {
validRanges[keys[i] + '_' + keyValues[i]] = [];
}
saxStream.onopentag = onOpenTag;
stream
.pipe(saxStream)
.on('end', doCallback);
function onOpenTag(node) {
var property;
if (validXMLTagNames[node.name]) {
for (var i in keys) {
property = keyValues[i];
if (node.attributes[keys[i]] === property) validProperty(keys[i] + '_' + property, node);
else invalidProperty(keys[i] + '_' + property);
}
}
}
function validProperty(property, node) {
if (!firstValid[property]) firstValid[property] =
node.attributes.cp || node.attributes['first-cp'];
lastValid[property] = node.attributes.cp || node.attributes['last-cp'];
}
function invalidProperty(property) {
if (!firstValid[property]) return;
validRanges[property].push([firstValid[property], lastValid[property]]);
firstValid[property] = null;
}
function doCallback() {
for (var i in keys) {
property = keys[i] + '_' + keyValues[i];
invalidProperty(property);
}
callback(validRanges);
}
}
+33
View File
@@ -0,0 +1,33 @@
exports.generateCode = generateCode;
exports.generateFunction = generateFunction;
function generateCode(validRanges) {
var code = '/******************************************************\n' +
' * Generated file, do not modify *\n' +
' * *\n' +
' *****************************************************/\n' +
'"use strict";\n';
var keys = Object.keys(validRanges);
for (var i in keys) {
code += generateFunction(validRanges[keys[i]], keys[i]);
}
return code;
}
function generateFunction(positiveElements, functionName) {
var result = [];
result.push('function ', functionName, '(cp) {\n');
positiveElements.forEach(function(range) {
if (range[0] === range[1]) {
result.push(' if (cp === 0x', range[0], ')');
} else {
result.push(' if (0x', range[0], ' <= cp && cp <= 0x', range[1], ')');
}
result.push(' return true;\n');
});
result.push(' return false;\n}\n');
return result.join('');
}
Binary file not shown.
+2056 -4338
View File
File diff suppressed because it is too large Load Diff
+3754 -7281
View File
File diff suppressed because it is too large Load Diff
+14 -9
View File
@@ -1,9 +1,9 @@
{
"name": "angularjs",
"license": "MIT",
"branchVersion": "^1.5.0-beta.2",
"branchVersion": "1.5.x",
"branchPattern": "1.5.*",
"distTag": "beta",
"distTag": "latest",
"repository": {
"type": "git",
"url": "https://github.com/angular/angular.js.git"
@@ -17,7 +17,8 @@
"preinstall": "node scripts/npm/check-node-modules.js --purge",
"postinstall": "node scripts/npm/copy-npm-shrinkwrap.js",
"commit": "git-cz",
"test-i18n": "jasmine-node i18n/spec"
"test-i18n": "jasmine-node i18n/spec",
"test-i18n-ucd": "jasmine-node i18n/ucd/spec"
},
"devDependencies": {
"angular-benchpress": "0.x.x",
@@ -29,7 +30,7 @@
"commitizen": "^2.3.0",
"cz-conventional-changelog": "1.1.4",
"dgeni": "^0.4.0",
"dgeni-packages": "^0.11.0",
"dgeni-packages": "^0.12.0",
"event-stream": "~3.1.0",
"glob": "^6.0.1",
"grunt": "~0.4.2",
@@ -38,7 +39,7 @@
"grunt-contrib-compress": "~0.12.0",
"grunt-contrib-connect": "~0.8.0",
"grunt-contrib-copy": "~0.6.0",
"grunt-contrib-jshint": "~0.10.0",
"grunt-contrib-jshint": "^1.0.0",
"grunt-ddescribe-iit": "~0.0.1",
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
"grunt-jscs": "^2.1.0",
@@ -47,19 +48,21 @@
"gulp": "~3.8.0",
"gulp-concat": "^2.4.1",
"gulp-foreach": "0.0.1",
"gulp-jshint": "~1.4.2",
"gulp-jshint": "^2.0.0",
"gulp-rename": "^1.2.0",
"gulp-sourcemaps": "^1.2.2",
"gulp-uglify": "^1.0.1",
"gulp-util": "^3.0.1",
"jasmine-core": "^2.4.0",
"jasmine-node": "^2.0.0",
"jasmine-reporters": "~1.0.1",
"jshint-stylish": "~1.0.0",
"jshint": "^2.9.1",
"jshint-stylish": "^2.1.0",
"karma": "^0.13.19",
"karma-browserstack-launcher": "^0.1.8",
"karma-chrome-launcher": "^0.2.2",
"karma-firefox-launcher": "^0.1.7",
"karma-jasmine": "^0.1.6",
"karma-jasmine": "^0.3.7",
"karma-junit-reporter": "^0.3.8",
"karma-ng-scenario": "^0.1.0",
"karma-sauce-launcher": "^0.3.0",
@@ -74,10 +77,12 @@
"q-io": "^1.10.9",
"qq": "^0.3.5",
"rewire": "~2.1.0",
"sax": "^1.1.1",
"semver": "~4.0.3",
"shelljs": "~0.3.0",
"sorted-object": "^1.0.0",
"stringmap": "^0.2.2"
"stringmap": "^0.2.2",
"strip-json-comments": "^2.0.1"
},
"licenses": [
{
+6 -6
View File
@@ -9,7 +9,7 @@ if (process.env.BROWSER_PROVIDER === 'browserstack') {
capabilitiesForBrowserStack({
browserName: 'chrome',
platform: 'MAC',
version: '34'
version: '49'
}),
capabilitiesForBrowserStack({
browserName: 'firefox',
@@ -18,7 +18,7 @@ if (process.env.BROWSER_PROVIDER === 'browserstack') {
capabilitiesForBrowserStack({
browserName: 'safari',
platform: 'MAC',
version: '7'
version: '9'
})
];
} else {
@@ -28,8 +28,8 @@ if (process.env.BROWSER_PROVIDER === 'browserstack') {
config.multiCapabilities = [
capabilitiesForSauceLabs({
browserName: 'chrome',
platform: 'OS X 10.9',
version: '34'
platform: 'OS X 10.11',
version: '48'
}),
capabilitiesForSauceLabs({
browserName: 'firefox',
@@ -37,8 +37,8 @@ if (process.env.BROWSER_PROVIDER === 'browserstack') {
}),
capabilitiesForSauceLabs({
browserName: 'safari',
platform: 'OS X 10.9',
version: '7'
platform: 'OS X 10.11',
version: '9'
})
];
}
+1
View File
@@ -11,6 +11,7 @@ REPOS=(
angular-message-format
angular-messages
angular-mocks
angular-parse-ext
angular-resource
angular-route
angular-sanitize
+2 -2
View File
@@ -4,11 +4,11 @@ echo "#################################"
echo "#### Jenkins Build ############"
echo "#################################"
source scripts/jenkins/set-node-version.sh
# 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
+1 -1
View File
@@ -35,7 +35,7 @@ function init {
}
function build {
./set-node-version.sh
source ./set-node-version.sh
cd ../..
npm install -g grunt-cli
+1 -1
View File
@@ -4,4 +4,4 @@
source ~/.nvm/nvm.sh
# Use node.js at 4.2.x
nvm install 4.2
nvm install 4.4
+2 -1
View File
@@ -1,7 +1,8 @@
{
"extends": "../.jshintrc-base",
"browser": true,
"globals": {
"window": false,
/* auto/injector.js */
"createInjector": false,
+45 -9
View File
@@ -102,6 +102,7 @@
* @ngdoc module
* @name ng
* @module ng
* @installation
* @description
*
* # ng (core module)
@@ -168,7 +169,7 @@ var
* documentMode is an IE-only property
* http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
*/
msie = document.documentMode;
msie = window.document.documentMode;
/**
@@ -457,7 +458,7 @@ function identity($) {return $;}
identity.$inject = [];
function valueFn(value) {return function() {return value;};}
function valueFn(value) {return function valueRef() {return value;};}
function hasCustomToString(obj) {
return isFunction(obj.toString) && obj.toString !== toString;
@@ -819,7 +820,7 @@ function copy(source, destination) {
function copyRecurse(source, destination) {
var h = destination.$$hashKey;
var result, key;
var key;
if (isArray(source)) {
for (var i = 0, ii = source.length; i < ii; i++) {
destination.push(copyElement(source[i]));
@@ -978,6 +979,41 @@ function shallowCopy(src, dst) {
* @param {*} o1 Object or value to compare.
* @param {*} o2 Object or value to compare.
* @returns {boolean} True if arguments are equal.
*
* @example
<example module="equalsExample" name="equalsExample">
<file name="index.html">
<div ng-controller="ExampleController">
<form novalidate>
<h3>User 1</h3>
Name: <input type="text" ng-model="user1.name">
Age: <input type="number" ng-model="user1.age">
<h3>User 2</h3>
Name: <input type="text" ng-model="user2.name">
Age: <input type="number" ng-model="user2.age">
<div>
<br/>
<input type="button" value="Compare" ng-click="compare()">
</div>
User 1: <pre>{{user1 | json}}</pre>
User 2: <pre>{{user2 | json}}</pre>
Equal: <pre>{{result}}</pre>
</form>
</div>
</file>
<file name="script.js">
angular.module('equalsExample', []).controller('ExampleController', ['$scope', function($scope) {
$scope.user1 = {};
$scope.user2 = {};
$scope.result;
$scope.compare = function() {
$scope.result = angular.equals($scope.user1, $scope.user2);
};
}]);
</file>
</example>
*/
function equals(o1, o2) {
if (o1 === o2) return true;
@@ -1024,8 +1060,8 @@ var csp = function() {
if (!isDefined(csp.rules)) {
var ngCspElement = (document.querySelector('[ng-csp]') ||
document.querySelector('[data-ng-csp]'));
var ngCspElement = (window.document.querySelector('[ng-csp]') ||
window.document.querySelector('[data-ng-csp]'));
if (ngCspElement) {
var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
@@ -1100,7 +1136,7 @@ var jq = function() {
var i, ii = ngAttrPrefixes.length, prefix, name;
for (i = 0; i < ii; ++i) {
prefix = ngAttrPrefixes[i];
if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
if (el = window.document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
name = el.getAttribute(prefix + 'jq');
break;
}
@@ -1165,7 +1201,7 @@ function toJsonReplacer(key, value) {
val = undefined;
} else if (isWindow(value)) {
val = '$WINDOW';
} else if (value && document === value) {
} else if (value && window.document === value) {
val = '$DOCUMENT';
} else if (isScope(value)) {
val = '$SCOPE';
@@ -1617,11 +1653,11 @@ function bootstrap(element, modules, config) {
element = jqLite(element);
if (element.injector()) {
var tag = (element[0] === document) ? 'document' : startingTag(element);
var tag = (element[0] === window.document) ? 'document' : startingTag(element);
//Encode angle brackets to prevent input from being sanitized to empty string #8683
throw ngMinErr(
'btstrpd',
"App Already Bootstrapped with this Element '{0}'",
"App already bootstrapped with this element '{0}'",
tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
}
+1 -1
View File
@@ -3,4 +3,4 @@
* (c) 2010-2016 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, document, undefined) {
(function(window) {
+3 -3
View File
@@ -1,5 +1,5 @@
jqLite(document).ready(function() {
angularInit(document, bootstrap);
jqLite(window.document).ready(function() {
angularInit(window.document, bootstrap);
});
})(window, document);
})(window);
+2 -1
View File
@@ -57,6 +57,7 @@
/**
* @ngdoc module
* @name auto
* @installation
* @description
*
* Implicit module which gets automatically added to each {@link auto.$injector $injector}.
@@ -70,7 +71,7 @@ var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
var $injectorMinErr = minErr('$injector');
function extractArgs(fn) {
var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
var fnText = Function.prototype.toString.call(fn).replace(STRIP_COMMENTS, ''),
args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
return args;
}
+7 -4
View File
@@ -114,6 +114,9 @@
* - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
* parent element is reached.
*
* @knownIssue You cannot spy on `angular.element` if you are using Jasmine version 1.x. See
* https://github.com/angular/angular.js/issues/14251 for more information.
*
* @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
* @returns {Object} jQuery object.
*/
@@ -240,7 +243,7 @@ function jqLiteBuildFragment(html, context) {
}
function jqLiteParseHTML(html, context) {
context = context || document;
context = context || window.document;
var parsed;
if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
@@ -266,7 +269,7 @@ function jqLiteWrapNode(node, wrapper) {
// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
var jqLiteContains = Node.prototype.contains || function(arg) {
var jqLiteContains = window.Node.prototype.contains || function(arg) {
// jshint bitwise: false
return !!(this.compareDocumentPosition(arg) & 16);
// jshint bitwise: true
@@ -538,8 +541,8 @@ var JQLitePrototype = JQLite.prototype = {
}
// check if document is already loaded
if (document.readyState === 'complete') {
setTimeout(trigger);
if (window.document.readyState === 'complete') {
window.setTimeout(trigger);
} else {
this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
// we can not use jqLite since we are not done loading and jQuery could be loaded later.
+3 -3
View File
@@ -197,9 +197,9 @@ function setupModuleLoader(window) {
* @ngdoc method
* @name angular.Module#decorator
* @module ng
* @param {string} The name of the service to decorate.
* @param {Function} This function will be invoked when the service needs to be
* instantiated and should return the decorated service instance.
* @param {string} name The name of the service to decorate.
* @param {Function} decorFn This function will be invoked when the service needs to be
* instantiated and should return the decorated service instance.
* @description
* See {@link auto.$provide#decorator $provide.decorator()}.
*/
+1 -1
View File
@@ -3,4 +3,4 @@
* (c) 2010-2016 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, angular, undefined) {
(function(window, angular) {
+6 -1
View File
@@ -326,6 +326,9 @@ var $AnimateProvider = ['$provide', function($provide) {
* // remove all the animation event listeners listening for `enter`
* $animate.off('enter');
*
* // remove listeners for all animation events from the container element
* $animate.off(container);
*
* // remove all the animation event listeners listening for `enter` on the given element and its children
* $animate.off('enter', container);
*
@@ -334,7 +337,9 @@ var $AnimateProvider = ['$provide', function($provide) {
* $animate.off('enter', container, callback);
* ```
*
* @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
* @param {string|DOMElement} event|container the animation event (e.g. enter, leave, move,
* addClass, removeClass, etc...), or the container element. If it is the element, all other
* arguments are ignored.
* @param {DOMElement=} container the container element the event listener was placed on
* @param {Function=} callback the callback function that was registered as the listener
*/
+8 -10
View File
@@ -24,7 +24,6 @@
*/
function Browser(window, document, $log, $sniffer) {
var self = this,
rawDocument = document[0],
location = window.location,
history = window.history,
setTimeout = window.setTimeout,
@@ -87,7 +86,14 @@ function Browser(window, document, $log, $sniffer) {
var cachedState, lastHistoryState,
lastBrowserUrl = location.href,
baseElement = document.find('base'),
pendingLocation = null;
pendingLocation = null,
getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
try {
return history.state;
} catch (e) {
// MSIE can reportedly throw when there is no state (UNCONFIRMED).
}
};
cacheState();
lastHistoryState = cachedState;
@@ -195,14 +201,6 @@ function Browser(window, document, $log, $sniffer) {
fireUrlChange();
}
function getCurrentState() {
try {
return history.state;
} catch (e) {
// MSIE can reportedly throw when there is no state (UNCONFIRMED).
}
}
// This variable should be used *only* inside the cacheState function.
var lastCachedState = null;
function cacheState() {
+227 -47
View File
@@ -293,9 +293,23 @@
* `true` if the specified slot contains content (i.e. one or more DOM nodes).
*
* The controller can provide the following methods that act as life-cycle hooks:
* * `$onInit` - Called on each controller after all the controllers on an element have been constructed and
* * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
* had their bindings initialized (and before the pre &amp; post linking functions for the directives on
* this element). This is a good place to put initialization code for your controller.
* * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
* `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
* object of the form `{ currentValue, previousValue, isFirstChange() }`. Use this hook to trigger updates within a
* component such as cloning the bound value to prevent accidental mutation of the outer value.
* * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
* external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
* the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
* components will have their `$onDestroy()` hook called before child components.
* * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
* function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
* Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
* they are waiting for their template to load asynchronously and their own compilation and linking has been
* suspended until that occurs.
*
*
* #### `require`
* Require another directive and inject its controller as the fourth argument to the linking function. The
@@ -832,6 +846,9 @@
var $compileMinErr = minErr('$compile');
function UNINITIALIZED_VALUE() {}
var _UNINITIALIZED_VALUE = new UNINITIALIZED_VALUE();
/**
* @ngdoc provider
* @name $compileProvider
@@ -856,7 +873,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
function parseIsolateBindings(scope, directiveName, isController) {
var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
var bindings = {};
var bindings = createMap();
forEach(scope, function(definition, scopeName) {
if (definition in bindingCache) {
@@ -928,11 +945,11 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
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);
throw $compileMinErr('baddir', "Directive/Component name '{0}' is invalid. The first character must be a lowercase letter", name);
}
if (name !== name.trim()) {
throw $compileMinErr('baddir',
"Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
"Directive/Component name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
name);
}
}
@@ -1075,7 +1092,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
*/
this.component = function registerComponent(name, options) {
var controller = options.controller || noop;
var controller = options.controller || function() {};
function factory($injector) {
function makeInjectable(fn) {
@@ -1089,7 +1106,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
var template = (!options.template && !options.templateUrl ? '' : options.template);
return {
var ddo = {
controller: controller,
controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
template: makeInjectable(template),
@@ -1100,14 +1117,27 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
restrict: 'E',
require: options.require
};
// Copy annotations (starting with $) over to the DDO
forEach(options, function(val, key) {
if (key.charAt(0) === '$') ddo[key] = val;
});
return ddo;
}
// Copy any annotation properties (starting with $) over to the factory function
// TODO(pete) remove the following `forEach` before we release 1.6.0
// The component-router@0.2.0 looks for the annotations on the controller constructor
// Nothing in Angular looks for annotations on the factory function but we can't remove
// it from 1.5.x yet.
// Copy any annotation properties (starting with $) over to the factory and controller constructor functions
// These could be used by libraries such as the new component router
forEach(options, function(val, key) {
if (key.charAt(0) === '$') {
factory[key] = val;
controller[key] = val;
// Don't try to copy over annotations to named controller
if (isFunction(controller)) controller[key] = val;
}
});
@@ -1207,6 +1237,36 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
return debugInfoEnabled;
};
var TTL = 10;
/**
* @ngdoc method
* @name $compileProvider#onChangesTtl
* @description
*
* Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
* assuming that the model is unstable.
*
* The current default is 10 iterations.
*
* In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
* in several iterations of calls to these hooks. However if an application needs more than the default 10
* iterations to stabilize then you should investigate what is causing the model to continuously change during
* the `$onChanges` hook execution.
*
* Increasing the TTL could have performance implications, so you should not change it without proper justification.
*
* @param {number} limit The number of `$onChanges` hook iterations.
* @returns {number|object} the current limit (or `this` if called as a setter for chaining)
*/
this.onChangesTtl = function(value) {
if (arguments.length) {
TTL = value;
return this;
}
return TTL;
};
this.$get = [
'$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
'$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
@@ -1214,8 +1274,38 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
$controller, $rootScope, $sce, $animate, $$sanitizeUri) {
var SIMPLE_ATTR_NAME = /^\w/;
var specialAttrHolder = document.createElement('div');
var Attributes = function(element, attributesToCopy) {
var specialAttrHolder = window.document.createElement('div');
var onChangesTtl = TTL;
// The onChanges hooks should all be run together in a single digest
// When changes occur, the call to trigger their hooks will be added to this queue
var onChangesQueue;
// This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
function flushOnChangesQueue() {
try {
if (!(--onChangesTtl)) {
// We have hit the TTL limit so reset everything
onChangesQueue = undefined;
throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
}
// We must run this hook in an apply since the $$postDigest runs outside apply
$rootScope.$apply(function() {
for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
onChangesQueue[i]();
}
// Reset the queue to trigger a new schedule next time there is a change
onChangesQueue = undefined;
});
} finally {
onChangesTtl++;
}
}
function Attributes(element, attributesToCopy) {
if (attributesToCopy) {
var keys = Object.keys(attributesToCopy);
var i, l, key;
@@ -1229,7 +1319,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
this.$$element = element;
};
}
Attributes.prototype = {
/**
@@ -1515,7 +1605,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (debugInfoEnabled) {
content = ' ' + (directiveName || '') + ': ' + (comment || '') + ' ';
}
return document.createComment(content);
return window.document.createComment(content);
};
return compile;
@@ -1529,6 +1619,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// modify it.
$compileNodes = jqLite($compileNodes);
}
var NOT_EMPTY = /\S+/;
// We can not compile top level text elements since text nodes can be merged and we will
// not be able to attach scope data to them, so we will wrap them in <span>
for (var i = 0, len = $compileNodes.length; i < len; i++) {
var domNode = $compileNodes[i];
if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
jqLiteWrapNode(domNode, $compileNodes[i] = window.document.createElement('span'));
}
}
var compositeLinkFn =
compileNodes($compileNodes, transcludeFn, $compileNodes,
maxPriority, ignoreDirective, previousCompileContext);
@@ -1718,8 +1821,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
if (!transcludedScope) {
transcludedScope = scope.$new(false, containingScope);
@@ -1731,7 +1833,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
transcludeControllers: controllers,
futureParentElement: futureParentElement
});
};
}
// We need to attach the transclusion slots onto the `boundTranscludeFn`
// so that they are available inside the `controllersBoundTransclude` function
@@ -1896,7 +1998,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* @returns {Function}
*/
function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
return function(scope, element, attrs, controllers, transcludeFn) {
return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
element = groupScan(element[0], attrStart, attrEnd);
return linkFn(scope, element, attrs, controllers, transcludeFn);
};
@@ -1919,7 +2021,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (eager) {
return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
}
return function() {
return function lazyCompilation() {
if (!compiled) {
compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
@@ -2068,6 +2170,17 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
compileNode = $compileNode[0];
replaceWith(jqCollection, sliceArgs($template), compileNode);
// Support: Chrome < 50
// https://github.com/angular/angular.js/issues/14041
// In the versions of V8 prior to Chrome 50, the document fragment that is created
// in the `replaceWith` function is improperly garbage collected despite still
// being referenced by the `parentNode` property of all of the child nodes. By adding
// a reference to the fragment via a different property, we can avoid that incorrect
// behavior.
// TODO: remove this line after Chrome 50 has been released
$template[0].$$parentNode = $template[0].parentNode;
childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
replaceDirective && replaceDirective.name, {
// Don't pass in:
@@ -2208,7 +2321,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
replaceDirective = directive;
}
/* jshint -W021 */
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
/* jshint +W021 */
templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
controllerDirectives: controllerDirectives,
newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
@@ -2272,7 +2387,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
attrs, removeScopeBindingWatches, removeControllerBindingWatches;
attrs, scopeBindingInfo;
if (compileNode === linkNode) {
attrs = templateAttrs;
@@ -2311,23 +2426,26 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
compile.$$addScopeClass($element, true);
isolateScope.$$isolateBindings =
newIsolateScopeDirective.$$isolateBindings;
removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
scopeBindingInfo = initializeDirectiveBindings(scope, attrs, isolateScope,
isolateScope.$$isolateBindings,
newIsolateScopeDirective);
if (removeScopeBindingWatches) {
isolateScope.$on('$destroy', removeScopeBindingWatches);
if (scopeBindingInfo.removeWatches) {
isolateScope.$on('$destroy', scopeBindingInfo.removeWatches);
}
}
// Initialize bindToController bindings
// Initialize controllers
for (var name in elementControllers) {
var controllerDirective = controllerDirectives[name];
var controller = elementControllers[name];
var bindings = controllerDirective.$$bindings.bindToController;
// Initialize bindToController bindings
if (controller.identifier && bindings) {
removeControllerBindingWatches =
controller.bindingInfo =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
} else {
controller.bindingInfo = {};
}
var controllerResult = controller();
@@ -2335,11 +2453,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// If the controller constructor has a return value, overwrite the instance
// from setupControllers
controller.instance = controllerResult;
$element.data('$' + controllerDirective.name + 'Controller', controllerResult);
removeControllerBindingWatches && removeControllerBindingWatches();
removeControllerBindingWatches =
controller.bindingInfo.removeWatches && controller.bindingInfo.removeWatches();
controller.bindingInfo =
initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
}
// Store controllers on the $element data
// For transclude comment nodes this will be a noop and will be done at transclusion time
$element.data('$' + controllerDirective.name + 'Controller', controllerResult);
}
// Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
@@ -2350,10 +2471,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
});
// Trigger the `$onInit` method on all controllers that have one
// Handle the init and destroy lifecycle hooks on all controllers that have them
forEach(elementControllers, function(controller) {
if (isFunction(controller.instance.$onInit)) {
controller.instance.$onInit();
var controllerInstance = controller.instance;
if (isFunction(controllerInstance.$onChanges)) {
controllerInstance.$onChanges(controller.bindingInfo.initialChanges);
}
if (isFunction(controllerInstance.$onInit)) {
controllerInstance.$onInit();
}
if (isFunction(controllerInstance.$onDestroy)) {
controllerScope.$on('$destroy', function callOnDestroyHook() {
controllerInstance.$onDestroy();
});
}
});
@@ -2390,6 +2520,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
);
}
// Trigger $postLink lifecycle hooks
forEach(elementControllers, function(controller) {
var controllerInstance = controller.instance;
if (isFunction(controllerInstance.$postLink)) {
controllerInstance.$postLink();
}
});
// This is the function that is injected as `$transclude`.
// Note: all arguments are optional!
function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
@@ -2489,14 +2627,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
controller = attrs[directive.name];
}
var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
// For directives with element transclusion the element is a comment.
// In this case .data will not attach any data.
// Instead, we save the controllers for the element in a local hash and attach to .data
// later, once we have the actual element.
elementControllers[directive.name] = controllerInstance;
$element.data('$' + directive.name + 'Controller', controllerInstance.instance);
elementControllers[directive.name] = $controller(controller, locals, true, directive.controllerAs);
}
return elementControllers;
}
@@ -2795,7 +2926,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
switch (type) {
case 'svg':
case 'math':
var wrapper = document.createElement('div');
var wrapper = window.document.createElement('div');
wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
return wrapper.childNodes[0].childNodes;
default:
@@ -2939,7 +3070,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// - remove them from the DOM
// - allow them to still be traversed with .nextSibling
// - allow a single fragment.qSA to fetch all elements being removed
var fragment = document.createDocumentFragment();
var fragment = window.document.createDocumentFragment();
for (i = 0; i < removeCount; i++) {
fragment.appendChild(elementsToRemove[i]);
}
@@ -2985,7 +3116,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// only occurs for isolate scopes and new scopes with controllerAs.
function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
var removeWatchCollection = [];
forEach(bindings, function(definition, scopeName) {
var initialChanges = {};
var changes;
forEach(bindings, function initializeBinding(definition, scopeName) {
var attrName = definition.attrName,
optional = definition.optional,
mode = definition.mode, // @, =, or &
@@ -2999,7 +3132,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
destination[scopeName] = attrs[attrName] = void 0;
}
attrs.$observe(attrName, function(value) {
if (isString(value)) {
if (isString(value) || isBoolean(value)) {
var oldValue = destination[scopeName];
recordChanges(scopeName, value, oldValue);
destination[scopeName] = value;
}
});
@@ -3014,6 +3149,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// the value to boolean rather than a string, so we special case this situation
destination[scopeName] = lastValue;
}
initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
break;
case '=':
@@ -3027,7 +3163,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (parentGet.literal) {
compare = equals;
} else {
compare = function(a, b) { return a === b || (a !== a && b !== b); };
compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
}
parentSet = parentGet.assign || function() {
// reset the change, or we will throw this exception on every $digest
@@ -3069,9 +3205,16 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
parentGet = $parse(attrs[attrName]);
destination[scopeName] = parentGet(scope);
initialChanges[scopeName] = new SimpleChange(_UNINITIALIZED_VALUE, destination[scopeName]);
removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newParentValue) {
destination[scopeName] = newParentValue;
removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newValue, oldValue) {
if (newValue === oldValue) {
// If the new and old values are identical then this is the first time the watch has been triggered
// So instead we use the current value on the destination as the old value
oldValue = destination[scopeName];
}
recordChanges(scopeName, newValue, oldValue);
destination[scopeName] = newValue;
}, parentGet.literal);
removeWatchCollection.push(removeWatch);
@@ -3091,15 +3234,52 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
});
return removeWatchCollection.length && function removeWatches() {
for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
removeWatchCollection[i]();
function recordChanges(key, currentValue, previousValue) {
if (isFunction(destination.$onChanges) && currentValue !== previousValue) {
// If we have not already scheduled the top level onChangesQueue handler then do so now
if (!onChangesQueue) {
scope.$$postDigest(flushOnChangesQueue);
onChangesQueue = [];
}
// If we have not already queued a trigger of onChanges for this controller then do so now
if (!changes) {
changes = {};
onChangesQueue.push(triggerOnChangesHook);
}
// If the has been a change on this property already then we need to reuse the previous value
if (changes[key]) {
previousValue = changes[key].previousValue;
}
// Store this change
changes[key] = new SimpleChange(previousValue, currentValue);
}
}
function triggerOnChangesHook() {
destination.$onChanges(changes);
// Now clear the changes so that we schedule onChanges when more changes arrive
changes = undefined;
}
return {
initialChanges: initialChanges,
removeWatches: removeWatchCollection.length && function removeWatches() {
for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
removeWatchCollection[i]();
}
}
};
}
}];
}
function SimpleChange(previous, current) {
this.previousValue = previous;
this.currentValue = current;
}
SimpleChange.prototype.isFirstChange = function() { return this.previousValue === _UNINITIALIZED_VALUE; };
var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
/**
* Converts all accepted directives format into proper directive name.
+2 -2
View File
@@ -92,7 +92,7 @@ function $ControllerProvider() {
* It's just a simple call to {@link auto.$injector $injector}, but extracted into
* a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
*/
return function(expression, locals, later, ident) {
return function $controller(expression, locals, later, ident) {
// PRIVATE API:
// param `later` --- indicates that the controller's constructor is invoked at a later time.
// If true, $controller will allocate the object with the correct
@@ -143,7 +143,7 @@ function $ControllerProvider() {
}
var instantiate;
return instantiate = extend(function() {
return instantiate = extend(function $controllerInit() {
var result = $injector.invoke(expression, instance, locals, constructor);
if (result !== instance && (isObject(result) || isFunction(result))) {
instance = result;
+2 -2
View File
@@ -393,7 +393,7 @@ var inputType = {
}]);
</script>
<form name="myForm" ng-controller="DateController as dateCtrl">
<label for="exampleInput">Pick a between 8am and 5pm:</label>
<label for="exampleInput">Pick a time between 8am and 5pm:</label>
<input type="time" id="exampleInput" name="input" ng-model="example.value"
placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
<div role="alert">
@@ -1114,7 +1114,7 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
if (!$sniffer.android) {
var composing = false;
element.on('compositionstart', function(data) {
element.on('compositionstart', function() {
composing = true;
});
+5 -1
View File
@@ -78,7 +78,11 @@ function classDirective(name, selector) {
updateClasses(oldClasses, newClasses);
}
}
oldVal = shallowCopy(newVal);
if (isArray(newVal)) {
oldVal = newVal.map(function(v) { return shallowCopy(v); });
} else {
oldVal = shallowCopy(newVal);
}
}
}
};
+1 -1
View File
@@ -292,7 +292,7 @@ var ngIncludeFillContentDirective = ['$compile',
// support innerHTML, so detect this here and try to generate the contents
// specially.
$element.empty();
$compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
$compile(jqLiteBuildFragment(ctrl.template, window.document).childNodes)(scope,
function namespaceAdaptedClone(clone) {
$element.append(clone);
}, {futureParentElement: $element});
+4 -4
View File
@@ -265,9 +265,9 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
};
ngModelSet = function($scope, newValue) {
if (isFunction(parsedNgModel($scope))) {
invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
invokeModelSetter($scope, {$$$p: newValue});
} else {
parsedNgModelAssign($scope, ctrl.$modelValue);
parsedNgModelAssign($scope, newValue);
}
};
} else if (!parsedNgModel.assign) {
@@ -644,7 +644,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
setValidity(name, undefined);
validatorPromises.push(promise.then(function() {
setValidity(name, true);
}, function(error) {
}, function() {
allValid = false;
setValidity(name, false);
}));
@@ -1118,7 +1118,7 @@ var ngModelDirective = ['$rootScope', function($rootScope) {
});
}
element.on('blur', function(ev) {
element.on('blur', function() {
if (modelCtrl.$touched) return;
if ($rootScope.$$phase) {
+46 -102
View File
@@ -245,7 +245,7 @@ var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s
// jshint maxlen: 100
var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile, $document, $parse) {
function parseOptionsExpression(optionsExp, selectElement, scope) {
@@ -342,8 +342,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
var value = optionValues[key];
var locals = getLocals(optionValues[key], key);
var selectValue = getTrackByValueFn(optionValues[key], locals);
var locals = getLocals(value, key);
var selectValue = getTrackByValueFn(value, locals);
watchedArray.push(selectValue);
// Only need to watch the displayFn if there is a specific label expression
@@ -406,8 +406,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
// we can't just jqLite('<option>') since jqLite is not smart enough
// to create it in <select> and IE barfs otherwise.
var optionTemplate = document.createElement('option'),
optGroupTemplate = document.createElement('optgroup');
var optionTemplate = window.document.createElement('option'),
optGroupTemplate = window.document.createElement('optgroup');
function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
@@ -432,7 +432,10 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var options;
var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
// This stores the newly created options before they are appended to the select.
// Since the contents are removed from the fragment when it is appended,
// we only need to create it once.
var listFragment = $document[0].createDocumentFragment();
var renderEmptyOption = function() {
if (!providedEmptyOption) {
@@ -467,7 +470,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
selectCtrl.writeValue = function writeNgOptionsValue(value) {
var option = options.getOptionFromViewValue(value);
if (option && !option.disabled) {
if (option) {
// Don't update the option when it is already selected.
// For example, the browser will select the first option by default. In that case,
// most properties are set automatically - except the `selected` attribute, which we
@@ -529,7 +532,7 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
if (value) {
value.forEach(function(item) {
var option = options.getOptionFromViewValue(item);
if (option && !option.disabled) option.element.selected = true;
if (option) option.element.selected = true;
});
}
};
@@ -581,6 +584,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
emptyOption = jqLite(optionTemplate.cloneNode(false));
}
selectElement.empty();
// We need to do this here to ensure that the options object is defined
// when we first hit it in writeNgOptionsValue
updateOptions();
@@ -590,6 +595,12 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
// ------------------------------------------------------------------ //
function addOptionElement(option, parent) {
var optionElement = optionTemplate.cloneNode(false);
parent.appendChild(optionElement);
updateOptionElement(option, optionElement);
}
function updateOptionElement(option, element) {
option.element = element;
@@ -606,133 +617,66 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
if (option.value !== element.value) element.value = option.selectValue;
}
function addOrReuseElement(parent, current, type, templateElement) {
var element;
// Check whether we can reuse the next element
if (current && lowercase(current.nodeName) === type) {
// The next element is the right type so reuse it
element = current;
} else {
// The next element is not the right type so create a new one
element = templateElement.cloneNode(false);
if (!current) {
// There are no more elements so just append it to the select
parent.appendChild(element);
} else {
// The next element is not a group so insert the new one
parent.insertBefore(element, current);
}
}
return element;
}
function removeExcessElements(current) {
var next;
while (current) {
next = current.nextSibling;
jqLiteRemove(current);
current = next;
}
}
function skipEmptyAndUnknownOptions(current) {
var emptyOption_ = emptyOption && emptyOption[0];
var unknownOption_ = unknownOption && unknownOption[0];
// We cannot rely on the extracted empty option being the same as the compiled empty option,
// because the compiled empty option might have been replaced by a comment because
// it had an "element" transclusion directive on it (such as ngIf)
if (emptyOption_ || unknownOption_) {
while (current &&
(current === emptyOption_ ||
current === unknownOption_ ||
current.nodeType === NODE_TYPE_COMMENT ||
(nodeName_(current) === 'option' && current.value === ''))) {
current = current.nextSibling;
}
}
return current;
}
function updateOptions() {
var previousValue = options && selectCtrl.readValue();
// We must remove all current options, but cannot simply set innerHTML = null
// since the providedEmptyOption might have an ngIf on it that inserts comments which we
// must preserve.
// Instead, iterate over the current option elements and remove them or their optgroup
// parents
if (options) {
for (var i = options.items.length - 1; i >= 0; i--) {
var option = options.items[i];
if (option.group) {
jqLiteRemove(option.element.parentNode);
} else {
jqLiteRemove(option.element);
}
}
}
options = ngOptions.getOptions();
var groupMap = {};
var currentElement = selectElement[0].firstChild;
var groupElementMap = {};
// Ensure that the empty option is always there if it was explicitly provided
if (providedEmptyOption) {
selectElement.prepend(emptyOption);
}
currentElement = skipEmptyAndUnknownOptions(currentElement);
options.items.forEach(function updateOption(option) {
var group;
options.items.forEach(function addOption(option) {
var groupElement;
var optionElement;
if (isDefined(option.group)) {
// This option is to live in a group
// See if we have already created this group
group = groupMap[option.group];
groupElement = groupElementMap[option.group];
if (!group) {
if (!groupElement) {
// We have not already created this group
groupElement = addOrReuseElement(selectElement[0],
currentElement,
'optgroup',
optGroupTemplate);
// Move to the next element
currentElement = groupElement.nextSibling;
groupElement = optGroupTemplate.cloneNode(false);
listFragment.appendChild(groupElement);
// Update the label on the group element
groupElement.label = option.group;
// Store it for use later
group = groupMap[option.group] = {
groupElement: groupElement,
currentOptionElement: groupElement.firstChild
};
groupElementMap[option.group] = groupElement;
}
// So now we have a group for this option we add the option to the group
optionElement = addOrReuseElement(group.groupElement,
group.currentOptionElement,
'option',
optionTemplate);
updateOptionElement(option, optionElement);
// Move to the next element
group.currentOptionElement = optionElement.nextSibling;
addOptionElement(option, groupElement);
} else {
// This option is not in a group
optionElement = addOrReuseElement(selectElement[0],
currentElement,
'option',
optionTemplate);
updateOptionElement(option, optionElement);
// Move to the next element
currentElement = optionElement.nextSibling;
addOptionElement(option, listFragment);
}
});
// Now remove all excess options and group
Object.keys(groupMap).forEach(function(key) {
removeExcessElements(groupMap[key].currentOptionElement);
});
removeExcessElements(currentElement);
selectElement[0].appendChild(listFragment);
ngModelCtrl.$render();
+1 -1
View File
@@ -47,7 +47,7 @@
* <div ng-controller="ExampleController">
* <input ng-model="title" aria-label="title"> <br/>
* <textarea ng-model="text" aria-label="text"></textarea> <br/>
* <pane title="{{title}}"><span>{{text}}</span></pane>
* <pane title="{{title}}">{{text}}</pane>
* </div>
* </file>
* <file name="protractor.js" type="protractor">
+2 -2
View File
@@ -20,7 +20,7 @@ function chromeHack(optionElement) {
* added `<option>` elements, perhaps by an `ngRepeat` directive.
*/
var SelectController =
['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
['$element', '$scope', function($element, $scope) {
var self = this,
optionsMap = new HashMap();
@@ -34,7 +34,7 @@ var SelectController =
//
// We can't just jqLite('<option>') since jqLite is not smart enough
// to create it in <select> and IE barfs otherwise.
self.unknownOption = jqLite(document.createElement('option'));
self.unknownOption = jqLite(window.document.createElement('option'));
self.renderUnknownOption = function(val) {
var unknownVal = '? ' + hashKey(val) + ' ?';
self.unknownOption.val(unknownVal);
+4 -2
View File
@@ -93,7 +93,9 @@ function currencyFilter($locale) {
* @param {(number|string)=} fractionSize Number of decimal places to round the number to.
* If this is not provided then the fraction size is computed from the current locale's number
* formatting pattern. In the case of the default locale, it will be 3.
* @returns {string} Number rounded to fractionSize and places a “,” after each third digit.
* @returns {string} Number rounded to `fractionSize` appropriately formatted based on the current
* locale (e.g., in the en_US locale it will have "." as the decimal separator and
* include "," group separators after each third digit).
*
* @example
<example module="numberFilterExample">
@@ -323,7 +325,7 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
// format the integer digits with grouping separators
var groups = [];
if (digits.length > pattern.lgSize) {
if (digits.length >= pattern.lgSize) {
groups.unshift(digits.splice(-pattern.lgSize).join(''));
}
while (digits.length > pattern.gSize) {
+28 -2
View File
@@ -47,7 +47,7 @@ function $HttpParamSerializerProvider() {
forEachSorted(params, function(value, key) {
if (value === null || isUndefined(value)) return;
if (isArray(value)) {
forEach(value, function(v, k) {
forEach(value, function(v) {
parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
});
} else {
@@ -801,6 +801,12 @@ function $HttpProvider() {
* - **headers** `{Object}` Map of strings or functions which return strings representing
* HTTP headers to send to the server. If the return value of a function is null, the
* header will not be sent. Functions accept a config object as an argument.
* - **eventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest object.
* To bind events to the XMLHttpRequest upload object, use `uploadEventHandlers`.
* The handler will be called in the context of a `$apply` block.
* - **uploadEventHandlers** - `{Object}` - Event listeners to be bound to the XMLHttpRequest upload
* object. To bind events to the XMLHttpRequest object, use `eventHandlers`.
* The handler will be called in the context of a `$apply` block.
* - **xsrfHeaderName** `{string}` Name of HTTP header to populate with the XSRF token.
* - **xsrfCookieName** `{string}` Name of cookie containing the XSRF token.
* - **transformRequest**
@@ -1259,11 +1265,31 @@ function $HttpProvider() {
}
$httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
config.withCredentials, config.responseType);
config.withCredentials, config.responseType,
createApplyHandlers(config.eventHandlers),
createApplyHandlers(config.uploadEventHandlers));
}
return promise;
function createApplyHandlers(eventHandlers) {
if (eventHandlers) {
var applyHandlers = {};
forEach(eventHandlers, function(eventHandler, key) {
applyHandlers[key] = function() {
if (useApplyAsync) {
$rootScope.$applyAsync(eventHandler);
} else if ($rootScope.$$phase) {
eventHandler();
} else {
$rootScope.$apply(eventHandler);
}
};
});
return applyHandlers;
}
}
/**
* Callback registered to $httpBackend():
+9 -1
View File
@@ -54,7 +54,7 @@ function $HttpBackendProvider() {
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
// TODO(vojta): fix the signature
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
return function(method, url, post, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
$browser.$$incOutstandingRequestCount();
url = url || $browser.url();
@@ -114,6 +114,14 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
xhr.onerror = requestError;
xhr.onabort = requestError;
forEach(eventHandlers, function(value, key) {
xhr.addEventListener(key, value);
});
forEach(uploadEventHandlers, function(value, key) {
xhr.upload.addEventListener(key, value);
});
if (withCredentials) {
xhr.withCredentials = true;
}
+87 -9
View File
@@ -150,7 +150,7 @@ Lexer.prototype = {
this.readString(ch);
} else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
this.readNumber();
} else if (this.isIdent(ch)) {
} else if (this.isIdentifierStart(this.peekMultichar())) {
this.readIdent();
} else if (this.is(ch, '(){}[].,;:?')) {
this.tokens.push({index: this.index, text: ch});
@@ -194,12 +194,49 @@ Lexer.prototype = {
ch === '\n' || ch === '\v' || ch === '\u00A0');
},
isIdent: function(ch) {
isIdentifierStart: function(ch) {
return this.options.isIdentifierStart ?
this.options.isIdentifierStart(ch, this.codePointAt(ch)) :
this.isValidIdentifierStart(ch);
},
isValidIdentifierStart: function(ch) {
return ('a' <= ch && ch <= 'z' ||
'A' <= ch && ch <= 'Z' ||
'_' === ch || ch === '$');
},
isIdentifierContinue: function(ch) {
return this.options.isIdentifierContinue ?
this.options.isIdentifierContinue(ch, this.codePointAt(ch)) :
this.isValidIdentifierContinue(ch);
},
isValidIdentifierContinue: function(ch, cp) {
return this.isValidIdentifierStart(ch, cp) || this.isNumber(ch);
},
codePointAt: function(ch) {
if (ch.length === 1) return ch.charCodeAt(0);
/*jshint bitwise: false*/
return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35FDC00;
/*jshint bitwise: true*/
},
peekMultichar: function() {
var ch = this.text.charAt(this.index);
var peek = this.peek();
if (!peek) {
return ch;
}
var cp1 = ch.charCodeAt(0);
var cp2 = peek.charCodeAt(0);
if (cp1 >= 0xD800 && cp1 <= 0xDBFF && cp2 >= 0xDC00 && cp2 <= 0xDFFF) {
return ch + peek;
}
return ch;
},
isExpOperator: function(ch) {
return (ch === '-' || ch === '+' || this.isNumber(ch));
},
@@ -248,12 +285,13 @@ Lexer.prototype = {
readIdent: function() {
var start = this.index;
this.index += this.peekMultichar().length;
while (this.index < this.text.length) {
var ch = this.text.charAt(this.index);
if (!(this.isIdent(ch) || this.isNumber(ch))) {
var ch = this.peekMultichar();
if (!this.isIdentifierContinue(ch)) {
break;
}
this.index++;
this.index += ch.length;
}
this.tokens.push({
index: start,
@@ -1183,7 +1221,13 @@ ASTCompiler.prototype = {
},
nonComputedMember: function(left, right) {
return left + '.' + right;
var SAFE_IDENTIFIER = /[$_a-zA-Z][$_a-zA-Z0-9]*/;
var UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
if (SAFE_IDENTIFIER.test(right)) {
return left + '.' + right;
} else {
return left + '["' + right.replace(UNSAFE_CHARACTERS, this.stringEscapeFn) + '"]';
}
},
computedMember: function(left, right) {
@@ -1449,7 +1493,7 @@ ASTInterpreter.prototype = {
return context ? {value: locals} : locals;
};
case AST.NGValueParameter:
return function(scope, locals, assign, inputs) {
return function(scope, locals, assign) {
return context ? {value: assign} : assign;
};
}
@@ -1746,6 +1790,7 @@ function $ParseProvider() {
'null': null,
'undefined': undefined
};
var identStart, identContinue;
/**
* @ngdoc method
@@ -1762,17 +1807,50 @@ function $ParseProvider() {
literals[literalName] = literalValue;
};
/**
* @ngdoc method
* @name $parseProvider#setIdentifierFns
* @description
*
* Allows defining the set of characters that are allowed in Angular expressions. The function
* `identifierStart` will get called to know if a given character is a valid character to be the
* first character for an identifier. The function `identifierContinue` will get called to know if
* a given character is a valid character to be a follow-up identifier character. The functions
* `identifierStart` and `identifierContinue` will receive as arguments the single character to be
* identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
* mind that the `string` parameter can be two characters long depending on the character
* representation. It is expected for the function to return `true` or `false`, whether that
* character is allowed or not.
*
* Since this function will be called extensivelly, keep the implementation of these functions fast,
* as the performance of these functions have a direct impact on the expressions parsing speed.
*
* @param {function=} identifierStart The function that will decide whether the given character is
* a valid identifier start character.
* @param {function=} identifierContinue The function that will decide whether the given character is
* a valid identifier continue character.
*/
this.setIdentifierFns = function(identifierStart, identifierContinue) {
identStart = identifierStart;
identContinue = identifierContinue;
return this;
};
this.$get = ['$filter', function($filter) {
var noUnsafeEval = csp().noUnsafeEval;
var $parseOptions = {
csp: noUnsafeEval,
expensiveChecks: false,
literals: copy(literals)
literals: copy(literals),
isIdentifierStart: isFunction(identStart) && identStart,
isIdentifierContinue: isFunction(identContinue) && identContinue
},
$parseOptionsExpensive = {
csp: noUnsafeEval,
expensiveChecks: true,
literals: copy(literals)
literals: copy(literals),
isIdentifierStart: isFunction(identStart) && identStart,
isIdentifierContinue: isFunction(identContinue) && identContinue
};
var runningChecksEnabled = false;
+3 -3
View File
@@ -13,15 +13,15 @@
* [Kris Kowal's Q](https://github.com/kriskowal/q).
*
* $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
* implementations, and the other which resembles ES6 promises to some degree.
* implementations, and the other which resembles ES6 (ES2015) promises to some degree.
*
* # $q constructor
*
* The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
* function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
* function as the first argument. This is similar to the native Promise implementation from ES6,
* see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
*
* While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
* While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
* available yet.
*
* It can be used like so:
+1 -1
View File
@@ -750,7 +750,7 @@ function $RootScopeProvider() {
dirty, ttl = TTL,
next, current, target = this,
watchLog = [],
logIdx, logMsg, asyncTask;
logIdx, asyncTask;
beginPhase('$digest');
// Check for changes to browser url that happened in sync before the call to $digest
+5 -1
View File
@@ -17,6 +17,10 @@
function $SnifferProvider() {
this.$get = ['$window', '$document', function($window, $document) {
var eventSupport = {},
// Chrome Packaged Apps are not allowed to access `history.pushState`. They can be detected by
// the presence of `chrome.app.runtime` (see https://developer.chrome.com/apps/api_index)
isChromePackagedApp = $window.chrome && $window.chrome.app && $window.chrome.app.runtime,
hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
android =
toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
@@ -61,7 +65,7 @@ function $SnifferProvider() {
// so let's not use the history API also
// We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
// jshint -W018
history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
history: !!(hasHistoryPushState && !(android < 4) && !boxee),
// jshint +W018
hasEvent: function(event) {
// IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
+2 -2
View File
@@ -1,6 +1,6 @@
'use strict';
var $compileMinErr = minErr('$compile');
var $templateRequestMinErr = minErr('$compile');
/**
* @ngdoc provider
@@ -96,7 +96,7 @@ function $TemplateRequestProvider() {
function handleError(resp) {
if (!ignoreRequestError) {
throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
throw $templateRequestMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
tpl, resp.status, resp.statusText);
}
return $q.reject(resp);
+1 -1
View File
@@ -6,7 +6,7 @@
// doesn't know about mocked locations and resolves URLs to the real document - which is
// exactly the behavior needed here. There is little value is mocking these out for this
// service.
var urlParsingNode = document.createElement("a");
var urlParsingNode = window.document.createElement("a");
var originUrl = urlResolve(window.location.href);
+2 -1
View File
@@ -2,8 +2,9 @@
"extends": "../../.jshintrc-base",
"maxlen": false, /* ngAnimate docs contain wide tables */
"newcap": false,
"browser": true,
"globals": {
"window": false,
"angular": false,
"noop": false,
+61 -17
View File
@@ -82,6 +82,11 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
});
rules.cancel.push(function(element, newAnimation, currentAnimation) {
// cancel the animation if classes added / removed in both animation cancel each other out,
// but only if the current animation isn't structural
if (currentAnimation.structural) return false;
var nA = newAnimation.addClass;
var nR = newAnimation.removeClass;
var cA = currentAnimation.addClass;
@@ -169,7 +174,7 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
}
// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
var contains = Node.prototype.contains || function(arg) {
var contains = window.Node.prototype.contains || function(arg) {
// jshint bitwise: false
return this === arg || !!(this.compareDocumentPosition(arg) & 16);
// jshint bitwise: true
@@ -194,7 +199,24 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
return matches;
}
return {
function filterFromRegistry(list, matchContainer, matchCallback) {
var containerNode = extractElementNode(matchContainer);
return list.filter(function(entry) {
var isMatch = entry.node === containerNode &&
(!matchCallback || entry.callback === matchCallback);
return !isMatch;
});
}
function cleanupEventListeners(phase, element) {
if (phase === 'close' && !element[0].parentNode) {
// If the element is not attached to a parentNode, it has been removed by
// the domOperation, and we can safely remove the event callbacks
$animate.off(element);
}
}
var $animate = {
on: function(event, container, callback) {
var node = extractElementNode(container);
callbackRegistry[event] = callbackRegistry[event] || [];
@@ -202,24 +224,36 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
node: node,
callback: callback
});
// Remove the callback when the element is removed from the DOM
jqLite(container).on('$destroy', function() {
var animationDetails = activeAnimationsLookup.get(node);
if (!animationDetails) {
// If there's an animation ongoing, the callback calling code will remove
// the event listeners. If we'd remove here, the callbacks would be removed
// before the animation ends
$animate.off(event, container, callback);
}
});
},
off: function(event, container, callback) {
if (arguments.length === 1 && !angular.isString(arguments[0])) {
container = arguments[0];
for (var eventType in callbackRegistry) {
callbackRegistry[eventType] = filterFromRegistry(callbackRegistry[eventType], container);
}
return;
}
var entries = callbackRegistry[event];
if (!entries) return;
callbackRegistry[event] = arguments.length === 1
? null
: filterFromRegistry(entries, container, callback);
function filterFromRegistry(list, matchContainer, matchCallback) {
var containerNode = extractElementNode(matchContainer);
return list.filter(function(entry) {
var isMatch = entry.node === containerNode &&
(!matchCallback || entry.callback === matchCallback);
return !isMatch;
});
}
},
pin: function(element, parentElement) {
@@ -269,6 +303,8 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
}
};
return $animate;
function queueAnimation(element, event, initialOptions) {
// we always make a copy of the options since
// there should never be any side effects on
@@ -331,12 +367,14 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
var isStructural = ['enter', 'move', 'leave'].indexOf(event) >= 0;
var documentHidden = $document[0].hidden;
// this is a hard disable of all animations for the application or on
// the element itself, therefore there is no need to continue further
// past this point if not enabled
// Animations are also disabled if the document is currently hidden (page is not visible
// to the user), because browsers slow down or do not flush calls to requestAnimationFrame
var skipAnimations = !animationsEnabled || $document[0].hidden || disabledElementsLookup.get(node);
var skipAnimations = !animationsEnabled || documentHidden || disabledElementsLookup.get(node);
var existingAnimation = (!skipAnimations && activeAnimationsLookup.get(node)) || {};
var hasExistingAnimation = !!existingAnimation.state;
@@ -347,7 +385,10 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
}
if (skipAnimations) {
// Callbacks should fire even if the document is hidden (regression fix for issue #14120)
if (documentHidden) notifyProgress(runner, event, 'start');
close();
if (documentHidden) notifyProgress(runner, event, 'close');
return runner;
}
@@ -497,6 +538,11 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
markElementAnimationState(element, RUNNING_STATE);
var realRunner = $$animation(element, event, animationDetails.options);
// this will update the runner's flow-control events based on
// the `realRunner` object.
runner.setHost(realRunner);
notifyProgress(runner, event, 'start', {});
realRunner.done(function(status) {
close(!status);
var animationDetails = activeAnimationsLookup.get(node);
@@ -505,11 +551,6 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
}
notifyProgress(runner, event, 'close', {});
});
// this will update the runner's flow-control events based on
// the `realRunner` object.
runner.setHost(realRunner);
notifyProgress(runner, event, 'start', {});
});
return runner;
@@ -526,7 +567,10 @@ var $$AnimateQueueProvider = ['$animateProvider', function($animateProvider) {
forEach(callbacks, function(callback) {
callback(element, phase, data);
});
cleanupEventListeners(phase, element);
});
} else {
cleanupEventListeners(phase, element);
}
});
runner.progress(event, phase, data);
+1 -1
View File
@@ -130,7 +130,7 @@
* <div ng-show="bool" class="fade">
* Show and hide me
* </div>
* <button ng-click="bool=true">Toggle</button>
* <button ng-click="bool=!bool">Toggle</button>
*
* <style>
* .fade.ng-hide {
+10 -3
View File
@@ -16,7 +16,7 @@
*
* For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
* directives are supported:
* `ngModel`, `ngChecked`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
* `ngModel`, `ngChecked`, `ngReadonly`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
* `ngDblClick`, and `ngMessages`.
*
* Below is a more detailed breakdown of the attributes handled by ngAria:
@@ -25,8 +25,9 @@
* |---------------------------------------------|----------------------------------------------------------------------------------------|
* | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
* | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled |
* | {@link ng.directive:ngRequired ngRequired} | aria-required |
* | {@link ng.directive:ngChecked ngChecked} | aria-checked |
* | {@link ng.directive:ngRequired ngRequired} | aria-required
* | {@link ng.directive:ngChecked ngChecked} | aria-checked
* | {@link ng.directive:ngReadonly ngReadonly} | aria-readonly ||
* | {@link ng.directive:ngValue ngValue} | aria-checked |
* | {@link ng.directive:ngShow ngShow} | aria-hidden |
* | {@link ng.directive:ngHide ngHide} | aria-hidden |
@@ -91,6 +92,7 @@ function $AriaProvider() {
var config = {
ariaHidden: true,
ariaChecked: true,
ariaReadonly: true,
ariaDisabled: true,
ariaRequired: true,
ariaInvalid: true,
@@ -108,6 +110,7 @@ function $AriaProvider() {
*
* - **ariaHidden** `{boolean}` Enables/disables aria-hidden tags
* - **ariaChecked** `{boolean}` Enables/disables aria-checked tags
* - **ariaReadonly** `{boolean}` Enables/disables aria-readonly tags
* - **ariaDisabled** `{boolean}` Enables/disables aria-disabled tags
* - **ariaRequired** `{boolean}` Enables/disables aria-required tags
* - **ariaInvalid** `{boolean}` Enables/disables aria-invalid tags
@@ -170,6 +173,7 @@ function $AriaProvider() {
* The full list of directives that interface with ngAria:
* * **ngModel**
* * **ngChecked**
* * **ngReadonly**
* * **ngRequired**
* * **ngDisabled**
* * **ngValue**
@@ -209,6 +213,9 @@ ngAriaModule.directive('ngShow', ['$aria', function($aria) {
.directive('ngChecked', ['$aria', function($aria) {
return $aria.$$watchExpr('ngChecked', 'aria-checked', nodeBlackList, false);
}])
.directive('ngReadonly', ['$aria', function($aria) {
return $aria.$$watchExpr('ngReadonly', 'aria-readonly', nodeBlackList, false);
}])
.directive('ngRequired', ['$aria', function($aria) {
return $aria.$$watchExpr('ngRequired', 'aria-required', nodeBlackList, false);
}])
+31 -3
View File
@@ -1,8 +1,36 @@
/**
* @ngdoc module
* @name ngComponentRouter
* @installation
* ## Installation
*
* Currently, the **Component Router** module must be installed via `npm`, it is not yet available
* on Bower or the Google CDN.
*
* ```bash
* npm install @angular/router --save
* ```
*
* Include `angular_1_router.js` in your HTML:
* ```html
* <script src="/node_modules/@angular/router/angular1/angular_1_router.js"></script>
*```
*
* You also need to include ES6 shims to support running on Internet Explorer:
* ```html
* <!-- IE required polyfills, in this exact order -->
* <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.min.js"></script>
* <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.20/system-polyfills.js"></script>
* <script src="https://npmcdn.com/angular2/es6/dev/src/testing/shims_for_IE.js"></script>
* ```
*
* Then load the module in your application by adding it as a dependent module:
*
* ```js
* angular.module('app', ['ngComponentRouter']);
* ```
*
* @description
* The new Angular Router
*/
/**
@@ -41,7 +69,7 @@
*
* There is only one instance of this type in a Component Router application injectable as the
* {@link $rootRouter} service. This **Router** is associate with the **Top Level Component**
* ({@link $routerRootComponent}). It acts as the connection betweent he **Routers** and the **Location**.
* ({@link $routerRootComponent}). It acts as the connection between the **Routers** and the **Location**.
*/
/**
@@ -62,7 +90,7 @@
* @name RouteDefinition
* @description
*
* Each item in a the **RouteConfig** for a **Routing Component** is an instance of
* Each item in the **RouteConfig** for a **Routing Component** is an instance of
* this type. It can have the following properties:
*
* * `path` or (`regex` and `serializer) - defines how to recognize and generate this route
+3 -2
View File
@@ -1,7 +1,8 @@
{
"extends": "../../.jshintrc-base",
"browser": true,
"globals": {
"window": false,
"angular": false
}
}
}
+3 -2
View File
@@ -2,9 +2,10 @@
"extends": "../../.jshintrc-base",
"bitwise": false, /* locale files use bitwise operators */
"maxlen": false, /* locale files are generated from a 3rd party library that has long lines */
"browser": true,
"globals": {
"window": false,
"angular": false
},
"-W041": false
}
}
+2 -1
View File
@@ -1,7 +1,8 @@
{
"extends": "../../.jshintrc-base",
"browser": true,
"globals": {
"window": false,
"angular": false,
"goog": false // see src/module_closure.prefix
}
+14 -3
View File
@@ -410,6 +410,13 @@ angular.module('ngMessages', [])
$scope.$watchCollection($attrs.ngMessages || $attrs['for'], ctrl.render);
// If the element is destroyed, proactively destroy all the currently visible messages
$element.on('$destroy', function() {
forEach(messages, function(item) {
item.message.detach();
});
});
this.reRender = function() {
if (!renderLater) {
renderLater = true;
@@ -444,6 +451,7 @@ angular.module('ngMessages', [])
function findPreviousMessage(parent, comment) {
var prevNode = comment;
var parentLookup = [];
while (prevNode && prevNode !== parent) {
var prevKey = prevNode.$$ngMessageNode;
if (prevKey && prevKey.length) {
@@ -455,8 +463,11 @@ angular.module('ngMessages', [])
if (prevNode.childNodes.length && parentLookup.indexOf(prevNode) == -1) {
parentLookup.push(prevNode);
prevNode = prevNode.childNodes[prevNode.childNodes.length - 1];
} else if (prevNode.previousSibling) {
prevNode = prevNode.previousSibling;
} else {
prevNode = prevNode.previousSibling || prevNode.parentNode;
prevNode = prevNode.parentNode;
parentLookup.push(prevNode);
}
}
}
@@ -669,8 +680,8 @@ function ngMessageDirectiveFactory() {
// when we are destroying the node later.
var $$attachId = currentElement.$$attachId = ngMessagesCtrl.getAttachId();
// in the event that the parent element is destroyed
// by any other structural directive then it's time
// in the event that the element or a parent element is destroyed
// by another structural directive then it's time
// to deregister the message from the controller
currentElement.on('$destroy', function() {
if (currentElement && currentElement.$$attachId === $$attachId) {
+3 -2
View File
@@ -1,9 +1,10 @@
{
"extends": "../../.jshintrc-base",
"browser": true,
"globals": {
"window": false,
"angular": false,
"expect": false,
"jQuery": false
}
}
}
+50 -18
View File
@@ -1321,12 +1321,15 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
}
// TODO(vojta): change params to: method, url, data, headers, callback
function $httpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType) {
function $httpBackend(method, url, data, callback, headers, timeout, withCredentials, responseType, eventHandlers, uploadEventHandlers) {
var xhr = new MockXhr(),
expectation = expectations[0],
wasExpected = false;
xhr.$$events = eventHandlers;
xhr.upload.$$events = uploadEventHandlers;
function prettyPrint(data) {
return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp)
? data
@@ -1416,12 +1419,14 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* order to change how a matched request is handled.
*
* - respond
* `{function([status,] data[, headers, statusText])
* | function(function(method, url, data, headers, params)}`
* ```js
* {function([status,] data[, headers, statusText])
* | function(function(method, url, data, headers, params)}
* ```
* The respond method takes a set of static data to be returned or a function that can
* return an array containing response status (number), response data (string), response
* headers (Object), and the text for the status (string). The respond method returns the
* `requestHandler` object for possible overrides.
* return an array containing response status (number), response data (Array|Object|string),
* response headers (Object), and the text for the status (string). The respond method returns
* the `requestHandler` object for possible overrides.
*/
$httpBackend.when = function(method, url, data, headers, keys) {
var definition = new MockHttpExpectation(method, url, data, headers, keys),
@@ -1606,12 +1611,14 @@ function createHttpBackendMock($rootScope, $timeout, $delegate, $browser) {
* order to change how a matched request is handled.
*
* - respond
* `{function([status,] data[, headers, statusText])
* | function(function(method, url, data, headers, params)}`
* ```
* { function([status,] data[, headers, statusText])
* | function(function(method, url, data, headers, params)}
* ```
* The respond method takes a set of static data to be returned or a function that can
* return an array containing response status (number), response data (string), response
* headers (Object), and the text for the status (string). The respond method returns the
* `requestHandler` object for possible overrides.
* return an array containing response status (number), response data (Array|Object|string),
* response headers (Object), and the text for the status (string). The respond method returns
* the `requestHandler` object for possible overrides.
*/
$httpBackend.expect = function(method, url, data, headers, keys) {
var expectation = new MockHttpExpectation(method, url, data, headers, keys),
@@ -2005,6 +2012,20 @@ function MockXhr() {
};
this.abort = angular.noop;
// This section simulates the events on a real XHR object (and the upload object)
// When we are testing $httpBackend (inside the angular project) we make partial use of this
// but store the events directly ourselves on `$$events`, instead of going through the `addEventListener`
this.$$events = {};
this.addEventListener = function(name, listener) {
if (angular.isUndefined(this.$$events[name])) this.$$events[name] = [];
this.$$events[name].push(listener);
};
this.upload = {
$$events: {},
addEventListener: this.addEventListener
};
}
@@ -2174,8 +2195,8 @@ angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
* A service that can be used to create instances of component controllers.
* <div class="alert alert-info">
* Be aware that the controller will be instantiated and attached to the scope as specified in
* the component definition object. That means that you must always provide a `$scope` object
* in the `locals` param.
* the component definition object. If you do not provide a `$scope` object in the `locals` param
* then the helper will create a new isolated scope as a child of `$rootScope`.
* </div>
* @param {string} componentName the name of the component whose controller we want to instantiate
* @param {Object} locals Injection locals for Controller.
@@ -2185,7 +2206,7 @@ angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
* @return {Object} Instance of requested controller.
*/
angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compileProvider) {
this.$get = ['$controller','$injector', function($controller,$injector) {
this.$get = ['$controller','$injector', '$rootScope', function($controller, $injector, $rootScope) {
return function $componentController(componentName, locals, bindings, ident) {
// get all directives associated to the component name
var directives = $injector.get(componentName + 'Directive');
@@ -2203,6 +2224,9 @@ angular.mock.$ComponentControllerProvider = ['$compileProvider', function($compi
}
// get the info of the component
var directiveInfo = candidateDirectives[0];
// create a scope if needed
locals = locals || {};
locals.$scope = locals.$scope || $rootScope.$new(true);
return $controller(directiveInfo.controller, locals, bindings, ident || directiveInfo.controllerAs);
};
}];
@@ -2326,11 +2350,13 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
* `respond` or `passThrough` again in order to change how a matched request is handled.
*
* - respond
* `{function([status,] data[, headers, statusText])
* | function(function(method, url, data, headers, params)}`
* ```
* { function([status,] data[, headers, statusText])
* | function(function(method, url, data, headers, params)}
* ```
* The respond method takes a set of static data to be returned or a function that can return
* an array containing response status (number), response data (string), response headers
* (Object), and the text for the status (string).
* an array containing response status (number), response data (Array|Object|string), response
* headers (Object), and the text for the status (string).
* - passThrough `{function()}` Any request matching a backend definition with
* `passThrough` handler will be passed through to the real backend (an XHR request will be made
* to the server.)
@@ -2892,6 +2918,12 @@ angular.mock.$RootScopeDecorator = ['$delegate', function($delegate) {
window.inject = angular.mock.inject = function() {
var blockFns = Array.prototype.slice.call(arguments, 0);
var errorForStack = new Error('Declaration Location');
// IE10+ and PhanthomJS do not set stack trace information, until the error is thrown
if (!errorForStack.stack) {
try {
throw errorForStack;
} catch (e) {}
}
return wasInjectorCreated() ? workFn.call(currentSpec) : workFn;
/////////////////////
function workFn() {
+45
View File
@@ -0,0 +1,45 @@
'use strict';
/**
* @ngdoc module
* @name ngParseExt
* @packageName angular-parse-ext
* @description
*
* # ngParseExt
*
* The `ngParseExt` module provides functionality to allow Unicode characters in
* identifiers inside Angular expressions.
*
*
* <div doc-module-components="ngParseExt"></div>
*
* This module allows the usage of any identifier that follows ES6 identifier naming convention
* to be used as an identifier in an Angular expression. ES6 delegates some of the identifier
* rules definition to Unicode, this module uses ES6 and Unicode 8.0 identifiers convention.
*
*/
/* global angularParseExtModule: true,
IDS_Y,
IDC_Y
*/
function isValidIdentifierStart(ch, cp) {
return ch === '$' ||
ch === '_' ||
IDS_Y(cp);
}
function isValidIdentifierContinue(ch, cp) {
return ch === '$' ||
ch === '_' ||
cp === 0x200C || // <ZWNJ>
cp === 0x200D || // <ZWJ>
IDC_Y(cp);
}
angular.module('ngParseExt', [])
.config(['$parseProvider', function($parseProvider) {
$parseProvider.setIdentifierFns(isValidIdentifierStart, isValidIdentifierContinue);
}]);
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -1,7 +1,8 @@
{
"extends": "../../.jshintrc-base",
"browser": true,
"globals": {
"window": false,
"angular": false
}
}
}
+3 -2
View File
@@ -1,8 +1,9 @@
{
"extends": "../../.jshintrc-base",
"browser": true,
"globals": {
"window": false,
"angular": false,
"ngRouteModule": false
}
}
}
+7
View File
@@ -26,6 +26,13 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
*
* The enter and leave animation occur concurrently.
*
* @knownIssue If `ngView` is contained in an asynchronously loaded template (e.g. in another
* directive's templateUrl or in a template loaded using `ngInclude`), then you need to
* make sure that `$route` is instantiated in time to capture the initial
* `$locationChangeStart` event and load the appropriate view. One way to achieve this
* is to have it as a dependency in a `.run` block:
* `myModule.run(['$route', function() {}]);`
*
* @scope
* @priority 400
* @param {string=} onload Expression to evaluate whenever the view updates.
+1 -5
View File
@@ -17,11 +17,7 @@
*/
/* global -ngRouteModule */
var ngRouteModule = angular.module('ngRoute', ['ng']).
provider('$route', $RouteProvider).
// Ensure `$route` will be instantiated in time to capture the initial
// `$locationChangeSuccess` event. This is necessary in case `ngView` is
// included in an asynchronously loaded template.
run(['$route', angular.noop]),
provider('$route', $RouteProvider),
$routeMinErr = angular.$$minErr('ngRoute');
/**
+3 -2
View File
@@ -1,8 +1,9 @@
{
"extends": "../../.jshintrc-base",
"browser": true,
"globals": {
"window": false,
"angular": false,
"htmlSanitizeWriter": false
}
}
}
+2 -2
View File
@@ -344,7 +344,7 @@ function htmlParser(html, handler) {
mXSSAttempts--;
// strip custom-namespaced attributes on IE<=11
if (document.documentMode <= 11) {
if (window.document.documentMode) {
stripCustomNsAttrs(inertBodyElement);
}
html = inertBodyElement.innerHTML; //trigger mXSS
@@ -484,7 +484,7 @@ function htmlSanitizeWriter(buf, uriValidator) {
* @param node Root element to process
*/
function stripCustomNsAttrs(node) {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.nodeType === window.Node.ELEMENT_NODE) {
var attrs = node.attributes;
for (var i = 0, l = attrs.length; i < l; i++) {
var attrNode = attrs[i];
+3 -2
View File
@@ -1,7 +1,8 @@
{
"extends": "../../.jshintrc-base",
"browser": true,
"globals": {
"window": false,
"angular": false,
"includes": false,
"asyncForEach": false,
@@ -19,4 +20,4 @@
"$runner": false,
"callerFile": false
}
}
}
+1 -1
View File
@@ -64,7 +64,7 @@ angular.scenario.Application.prototype.navigateTo = function(url, loadFn, errorF
self.context.find('#test-frames').append('<iframe>');
frame = self.getFrame_();
frame.load(function() {
frame.on('load', function() {
frame.off();
try {
var $window = self.getWindow_();
+1 -1
View File
@@ -104,7 +104,7 @@ angular.scenario.matcher = angular.scenario.matcher || function(name, fn) {
*/
angular.scenario.setUpAndRun = function(config) {
var href = window.location.href;
var body = _jQuery(document.body);
var body = _jQuery(window.document.body);
var output = [];
var objModel = new angular.scenario.ObjectModel($runner);
+5 -5
View File
@@ -5,7 +5,7 @@
(function(previousOnLoad) {
var prefix = (function() {
var filename = /(.*\/)angular-bootstrap.js(#(.*))?/;
var scripts = document.getElementsByTagName("script");
var scripts = window.document.getElementsByTagName("script");
for (var j = 0; j < scripts.length; j++) {
var src = scripts[j].src;
if (src && src.match(filename)) {
@@ -16,11 +16,11 @@
})();
function addScript(path) {
document.write('<script type="text/javascript" src="' + prefix + path + '"></script>');
window.document.write('<script type="text/javascript" src="' + prefix + path + '"></script>');
}
function addCSS(path) {
document.write('<link rel="stylesheet" type="text/css" href="' + prefix + path + '"/>');
window.document.write('<link rel="stylesheet" type="text/css" href="' + prefix + path + '"/>');
}
window.onload = function() {
@@ -32,7 +32,7 @@
addCSS("../../css/angular-scenario.css");
addScript("../../lib/jquery/jquery.js");
document.write(
window.document.write(
'<script type="text/javascript">' +
'var _jQuery = jQuery.noConflict(true);' +
'</script>'
@@ -54,7 +54,7 @@
addScript("output/Xml.js");
// Create the runner (which also sets up the global API)
document.write(
window.document.write(
'<script type="text/javascript">' +
' var $runner = new angular.scenario.Runner(window);' +
'</script>');
+1 -1
View File
@@ -3,5 +3,5 @@
* (c) 2010-2016 Google, Inc. http://angularjs.org
* License: MIT
*/
(function(window, document){
(function(window){
var _jQuery = window.jQuery.noConflict(true);
+3 -3
View File
@@ -2,7 +2,7 @@ bindJQuery();
publishExternalAPI(angular);
var $runner = new angular.scenario.Runner(window),
scripts = document.getElementsByTagName('script'),
scripts = window.document.getElementsByTagName('script'),
script = scripts[scripts.length - 1],
config = {};
@@ -14,9 +14,9 @@ angular.forEach(script.attributes, function(attr) {
});
if (config.autotest) {
JQLite(document).ready(function() {
JQLite(window.document).ready(function() {
angular.scenario.setUpAndRun(config);
});
}
})(window, document);
})(window);
+8 -8
View File
@@ -61,7 +61,7 @@
evnt = new TransitionEvent(eventType, eventData);
}
catch (e) {
evnt = document.createEvent('TransitionEvent');
evnt = window.document.createEvent('TransitionEvent');
evnt.initTransitionEvent(eventType, null, null, null, eventData.elapsedTime || 0);
}
}
@@ -74,14 +74,14 @@
evnt = new AnimationEvent(eventType, eventData);
}
catch (e) {
evnt = document.createEvent('AnimationEvent');
evnt = window.document.createEvent('AnimationEvent');
evnt.initAnimationEvent(eventType, null, null, null, eventData.elapsedTime || 0);
}
}
} else if (/touch/.test(eventType) && supportsTouchEvents()) {
evnt = createTouchEvent(element, eventType, x, y);
} else {
evnt = document.createEvent('MouseEvents');
evnt = window.document.createEvent('MouseEvents');
x = x || 0;
y = y || 0;
evnt.initMouseEvent(eventType, true, true, window, 0, x, y, x, y, pressed('ctrl'),
@@ -120,12 +120,12 @@
if ('_cached' in supportsTouchEvents) {
return supportsTouchEvents._cached;
}
if (!document.createTouch || !document.createTouchList) {
if (!window.document.createTouch || !window.document.createTouchList) {
supportsTouchEvents._cached = false;
return false;
}
try {
document.createEvent('TouchEvent');
window.document.createEvent('TouchEvent');
} catch (e) {
supportsTouchEvents._cached = false;
return false;
@@ -135,12 +135,12 @@
}
function createTouchEvent(element, eventType, x, y) {
var evnt = new Event(eventType);
var evnt = new window.Event(eventType);
x = x || 0;
y = y || 0;
var touch = document.createTouch(window, element, Date.now(), x, y, x, y);
var touches = document.createTouchList(touch);
var touch = window.document.createTouch(window, element, Date.now(), x, y, x, y);
var touches = window.document.createTouchList(touch);
evnt.touches = touches;
+1 -1
View File
@@ -199,7 +199,7 @@ angular.scenario.dsl('binding', function() {
*/
angular.scenario.dsl('input', function() {
var chain = {};
var supportInputEvent = 'oninput' in document.createElement('div') && !(msie && msie <= 11);
var supportInputEvent = 'oninput' in window.document.createElement('div') && !msie;
chain.enter = function(value, event) {
return this.addFutureAction("input '" + this.name + "' enter '" + value + "'",
+3 -2
View File
@@ -1,8 +1,9 @@
{
"extends": "../../.jshintrc-base",
"browser": true,
"globals": {
"window": false,
"angular": false,
"ngTouch": false
}
}
}
+5 -4
View File
@@ -131,9 +131,9 @@
/* jasmine / karma */
"it": false,
"iit": false,
"fit": false,
"describe": false,
"ddescribe": false,
"fdescribe": false,
"beforeEach": false,
"afterEach": false,
"expect": false,
@@ -144,7 +144,7 @@
"runs": false,
"dump": false,
"they": false,
"tthey": false,
"fthey": false,
"xthey": false,
"assertCompareNodes": false,
@@ -168,6 +168,7 @@
"createMockStyleSheet": false,
"browserSupportsCssAnimations": false,
"browserTrigger": false,
"jqLiteCacheSize": false
"jqLiteCacheSize": false,
"createAsync": false
}
}
+18 -18
View File
@@ -983,7 +983,7 @@ describe('angular', function() {
describe('csp', function() {
function mockCspElement(cspAttrName, cspAttrValue) {
return spyOn(document, 'querySelector').andCallFake(function(selector) {
return spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector == '[' + cspAttrName + ']') {
var html = '<div ' + cspAttrName + (cspAttrValue ? ('="' + cspAttrValue + '" ') : '') + '></div>';
return jqLite(html)[0];
@@ -1009,7 +1009,7 @@ describe('angular', function() {
it('should return true for noUnsafeEval if eval causes a CSP security policy error', function() {
window.Function.andCallFake(function() { throw new Error('CSP test'); });
window.Function.and.callFake(function() { throw new Error('CSP test'); });
expect(csp()).toEqual({ noUnsafeEval: true, noInlineStyle: false });
expect(window.Function).toHaveBeenCalledWith('');
});
@@ -1073,7 +1073,7 @@ describe('angular', function() {
it('should return empty string when jq is enabled manually via [ng-jq] with empty string', function() {
element.setAttribute('ng-jq', '');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[ng-jq]') return element;
});
expect(jq()).toBe('');
@@ -1081,7 +1081,7 @@ describe('angular', function() {
it('should return empty string when jq is enabled manually via [data-ng-jq] with empty string', function() {
element.setAttribute('data-ng-jq', '');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[data-ng-jq]') return element;
});
expect(jq()).toBe('');
@@ -1090,7 +1090,7 @@ describe('angular', function() {
it('should return empty string when jq is enabled manually via [x-ng-jq] with empty string', function() {
element.setAttribute('x-ng-jq', '');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[x-ng-jq]') return element;
});
expect(jq()).toBe('');
@@ -1099,7 +1099,7 @@ describe('angular', function() {
it('should return empty string when jq is enabled manually via [ng:jq] with empty string', function() {
element.setAttribute('ng:jq', '');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[ng\\:jq]') return element;
});
expect(jq()).toBe('');
@@ -1108,7 +1108,7 @@ describe('angular', function() {
it('should return "jQuery" when jq is enabled manually via [ng-jq] with value "jQuery"', function() {
element.setAttribute('ng-jq', 'jQuery');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[ng-jq]') return element;
});
expect(jq()).toBe('jQuery');
@@ -1117,7 +1117,7 @@ describe('angular', function() {
it('should return "jQuery" when jq is enabled manually via [data-ng-jq] with value "jQuery"', function() {
element.setAttribute('data-ng-jq', 'jQuery');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[data-ng-jq]') return element;
});
expect(jq()).toBe('jQuery');
@@ -1126,7 +1126,7 @@ describe('angular', function() {
it('should return "jQuery" when jq is enabled manually via [x-ng-jq] with value "jQuery"', function() {
element.setAttribute('x-ng-jq', 'jQuery');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[x-ng-jq]') return element;
});
expect(jq()).toBe('jQuery');
@@ -1135,7 +1135,7 @@ describe('angular', function() {
it('should return "jQuery" when jq is enabled manually via [ng:jq] with value "jQuery"', function() {
element.setAttribute('ng:jq', 'jQuery');
spyOn(document, 'querySelector').andCallFake(function(selector) {
spyOn(document, 'querySelector').and.callFake(function(selector) {
if (selector === '[ng\\:jq]') return element;
});
expect(jq()).toBe('jQuery');
@@ -1636,7 +1636,7 @@ describe('angular', function() {
expect(function() {
angularInit(appElement, angular.bootstrap);
}).toThrowMatching(
}).toThrowError(
new RegExp('\\[\\$injector:modulerr] Failed to instantiate module doesntexist due to:\\n' +
'.*\\[\\$injector:nomod] Module \'doesntexist\' is not available! You either ' +
'misspelled the module name or forgot to load it\\.')
@@ -1650,7 +1650,7 @@ describe('angular', function() {
expect(function() {
angular.bootstrap(element);
}).toThrowMatching(
}).toThrowError(
/\[ng:btstrpd\] App Already Bootstrapped with this Element '&lt;div class="?ng\-scope"?( ng[0-9]+="?[0-9]+"?)?&gt;'/i
);
@@ -1662,7 +1662,7 @@ describe('angular', function() {
angular.bootstrap(document.getElementsByTagName('html')[0]);
expect(function() {
angular.bootstrap(document);
}).toThrowMatching(
}).toThrowError(
/\[ng:btstrpd\] App Already Bootstrapped with this Element 'document'/i
);
@@ -1671,11 +1671,11 @@ describe('angular', function() {
it('should bootstrap in strict mode when ng-strict-di attribute is specified', function() {
bootstrapSpy = spyOn(angular, 'bootstrap').andCallThrough();
bootstrapSpy = spyOn(angular, 'bootstrap').and.callThrough();
var appElement = jqLite('<div ng-app="" ng-strict-di></div>');
angularInit(jqLite('<div></div>').append(appElement[0])[0], bootstrapSpy);
expect(bootstrapSpy).toHaveBeenCalledOnce();
expect(bootstrapSpy.mostRecentCall.args[2].strictDi).toBe(true);
expect(bootstrapSpy.calls.mostRecent().args[2].strictDi).toBe(true);
var injector = appElement.injector();
function testFactory($rootScope) {}
@@ -1863,7 +1863,7 @@ describe('angular', function() {
expect(function() {
angular.bootstrap(element, ['doesntexist']);
}).toThrowMatching(
}).toThrowError(
new RegExp('\\[\\$injector:modulerr\\] Failed to instantiate module doesntexist due to:\\n' +
'.*\\[\\$injector:nomod\\] Module \'doesntexist\' is not available! You either ' +
'misspelled the module name or forgot to load it\\.'));
@@ -1997,7 +1997,7 @@ describe('angular', function() {
describe('fromJson', function() {
it('should delegate to JSON.parse', function() {
var spy = spyOn(JSON, 'parse').andCallThrough();
var spy = spyOn(JSON, 'parse').and.callThrough();
expect(fromJson('{}')).toEqual({});
expect(spy).toHaveBeenCalled();
@@ -2008,7 +2008,7 @@ describe('angular', function() {
describe('toJson', function() {
it('should delegate to JSON.stringify', function() {
var spy = spyOn(JSON, 'stringify').andCallThrough();
var spy = spyOn(JSON, 'stringify').and.callThrough();
expect(toJson({})).toEqual('{}');
expect(spy).toHaveBeenCalled();
+1 -1
View File
@@ -186,7 +186,7 @@ describe('Binder', function() {
$rootScope.error['throw'] = function() { return 'X';};
$rootScope.$apply();
expect(errorLogs.length).toMatch(0);
expect(errorLogs.length).toMatch('0');
});
});
+13 -7
View File
@@ -84,14 +84,14 @@ describe('injector', function() {
});
it('should provide the caller name if given', function(done) {
it('should provide the caller name if given', function() {
expect(function() {
injector.get('idontexist', 'callerName');
}).toThrowMinErr("$injector", "unpr", "Unknown provider: idontexistProvider <- idontexist <- callerName");
});
it('should provide the caller name for controllers', function(done) {
it('should provide the caller name for controllers', function() {
controllerProvider.register('myCtrl', function(idontexist) {});
var $controller = injector.get('$controller');
expect(function() {
@@ -241,6 +241,12 @@ describe('injector', function() {
expect($f_n0.$inject).toEqual(['$a_']);
});
it('should handle functions with overridden toString', function() {
function fn(a) {}
fn.toString = function() { return 'fn'; };
expect(annotate(fn)).toEqual(['a']);
expect(fn.$inject).toEqual(['a']);
});
it('should throw on non function arg', function() {
expect(function() {
@@ -282,9 +288,9 @@ describe('injector', function() {
if (support.classes) {
it('should be possible to instantiate ES6 classes', function() {
providers('a', function() { return 'a-value'; });
var clazz = eval('(class { constructor(a) { this.a = a; } aVal() { return this.a; } })');
var instance = injector.instantiate(clazz);
expect(instance).toEqual({a: 'a-value'});
var Clazz = eval('(class { constructor(a) { this.a = a; } aVal() { return this.a; } })');
var instance = injector.instantiate(Clazz);
expect(instance).toEqual(new Clazz('a-value'));
expect(instance.aVal()).toEqual('a-value');
});
}
@@ -294,7 +300,7 @@ describe('injector', function() {
it('should publish annotate API', function() {
expect(angular.mock.$$annotate).toBe(annotate);
spyOn(angular.mock, '$$annotate').andCallThrough();
spyOn(angular.mock, '$$annotate').and.callThrough();
function fn() {}
injector.annotate(fn);
expect(angular.mock.$$annotate).toHaveBeenCalledWith(fn);
@@ -1012,7 +1018,7 @@ describe('injector', function() {
createInjector([function($provide) {
$provide.value('name', 'angular');
}, instanceLookupInModule]);
}).toThrowMatching(/\[\$injector:unpr] Unknown provider: name/);
}).toThrowError(/\[\$injector:unpr] Unknown provider: name/);
});
});
});
+1 -1
View File
@@ -1,3 +1,3 @@
{
"node": true
}
}
+303 -203
View File
@@ -4,24 +4,31 @@ beforeEach(function() {
function cssMatcher(presentClasses, absentClasses) {
return function() {
var element = angular.element(this.actual);
var present = true;
var absent = false;
return {
compare: function(actual) {
var element = angular.element(actual);
var present = true;
var absent = false;
angular.forEach(presentClasses.split(' '), function(className) {
present = present && element.hasClass(className);
});
angular.forEach(presentClasses.split(' '), function(className) {
present = present && element.hasClass(className);
});
angular.forEach(absentClasses.split(' '), function(className) {
absent = absent || element.hasClass(className);
});
angular.forEach(absentClasses.split(' '), function(className) {
absent = absent || element.hasClass(className);
});
this.message = function() {
return "Expected to have " + presentClasses +
(absentClasses ? (" and not have " + absentClasses + "") : "") +
" but had " + element[0].className + ".";
var message = function() {
return "Expected to have " + presentClasses +
(absentClasses ? (" and not have " + absentClasses + "") : "") +
" but had " + element[0].className + ".";
};
return {
pass: present && !absent,
message: message
};
}
};
return present && !absent;
};
}
@@ -36,7 +43,7 @@ beforeEach(function() {
return hidden;
}
this.addMatchers({
jasmine.addMatchers({
toBeEmpty: cssMatcher('ng-empty', 'ng-not-empty'),
toBeNotEmpty: cssMatcher('ng-not-empty', 'ng-empty'),
toBeInvalid: cssMatcher('ng-invalid', 'ng-valid'),
@@ -46,236 +53,329 @@ beforeEach(function() {
toBeUntouched: cssMatcher('ng-untouched', 'ng-touched'),
toBeTouched: cssMatcher('ng-touched', 'ng-untouched'),
toBeAPromise: function() {
this.message = valueFn(
"Expected object " + (this.isNot ? "not " : "") + "to be a promise");
return isPromiseLike(this.actual);
return {
compare: generateCompare(false),
negativeCompare: generateCompare(true)
};
function generateCompare(isNot) {
return function(actual) {
var message = valueFn(
"Expected object " + (isNot ? "not " : "") + "to be a promise");
return { pass: isPromiseLike(actual), message: message };
};
}
},
toBeShown: function() {
this.message = valueFn(
"Expected element " + (this.isNot ? "" : "not ") + "to have 'ng-hide' class");
return !isNgElementHidden(this.actual);
return {
compare: generateCompare(false),
negativeCompare: generateCompare(true)
};
function generateCompare(isNot) {
return function(actual) {
var message = valueFn("Expected element " + (isNot ? "" : "not ") + "to have 'ng-hide' class");
var pass = !isNgElementHidden(actual);
if (isNot) {
pass = !pass;
}
return { pass: pass, message: message };
};
}
},
toBeHidden: function() {
this.message = valueFn(
"Expected element " + (this.isNot ? "not " : "") + "to have 'ng-hide' class");
return isNgElementHidden(this.actual);
},
toEqual: function(expected) {
if (this.actual && this.actual.$$log) {
this.actual = (typeof expected === 'string')
? this.actual.toString()
: this.actual.toArray();
return {
compare: generateCompare(false),
negativeCompare: generateCompare(true)
};
function generateCompare(isNot) {
return function(actual) {
var message = valueFn("Expected element " + (isNot ? "not " : "") + "to have 'ng-hide' class");
var pass = isNgElementHidden(actual);
if (isNot) {
pass = !pass;
}
return { pass: pass, message: message };
};
}
return jasmine.Matchers.prototype.toEqual.call(this, expected);
},
toEqualData: function(expected) {
return angular.equals(this.actual, expected);
},
toEqualError: function(message) {
this.message = function() {
var expected;
if (this.actual.message && this.actual.name == 'Error') {
expected = angular.toJson(this.actual.message);
} else {
expected = angular.toJson(this.actual);
toEqual: function(util) {
return {
compare: function(actual, expected) {
if (actual && actual.$$log) {
actual = (typeof expected === 'string')
? actual.toString()
: actual.toArray();
}
return {
pass: util.equals(actual, expected, [DOMTester])
};
}
return "Expected " + expected + " to be an Error with message " + angular.toJson(message);
};
return this.actual.name == 'Error' && this.actual.message == message;
function DOMTester(a, b) {
if (a && b && a.nodeType > 0 && b.nodeType > 0) {
return a === b;
}
}
},
toMatchError: function(messageRegexp) {
this.message = function() {
var expected;
if (this.actual.message && this.actual.name == 'Error') {
expected = angular.toJson(this.actual.message);
} else {
expected = angular.toJson(this.actual);
toEqualData: function() {
return {
compare: function(actual, expected) {
return { pass: angular.equals(actual, expected) };
}
return "Expected " + expected + " to match an Error with message " + angular.toJson(messageRegexp);
};
return this.actual.name == 'Error' && messageRegexp.test(this.actual.message);
},
toHaveBeenCalledOnce: function() {
if (arguments.length > 0) {
throw new Error('toHaveBeenCalledOnce does not take arguments, use toHaveBeenCalledWith');
}
if (!jasmine.isSpy(this.actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
}
this.message = function() {
var msg = 'Expected spy ' + this.actual.identity + ' to have been called once, but was ',
count = this.actual.callCount;
return [
count === 0 ? msg + 'never called.' :
msg + 'called ' + count + ' times.',
msg.replace('to have', 'not to have') + 'called once.'
];
};
return this.actual.callCount == 1;
},
toHaveBeenCalledOnceWith: function() {
var expectedArgs = jasmine.util.argsToArray(arguments);
if (!jasmine.isSpy(this.actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
}
this.message = function() {
if (this.actual.callCount != 1) {
if (this.actual.callCount === 0) {
return [
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
jasmine.pp(expectedArgs) + ' but it was never called.',
'Expected spy ' + this.actual.identity + ' not to have been called with ' +
jasmine.pp(expectedArgs) + ' but it was.'
];
return {
compare: function(actual) {
if (arguments.length > 1) {
throw new Error('`toHaveBeenCalledOnce` does not take arguments, ' +
'use `toHaveBeenCalledOnceWith`');
}
return [
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
jasmine.pp(expectedArgs) + ' but it was called ' + this.actual.callCount + ' times.',
'Expected spy ' + this.actual.identity + ' not to have been called once with ' +
jasmine.pp(expectedArgs) + ' but it was.'
];
} else {
return [
'Expected spy ' + this.actual.identity + ' to have been called once with ' +
jasmine.pp(expectedArgs) + ' but was called with ' + jasmine.pp(this.actual.argsForCall),
'Expected spy ' + this.actual.identity + ' not to have been called once with ' +
jasmine.pp(expectedArgs) + ' but was called with ' + jasmine.pp(this.actual.argsForCall)
];
if (!jasmine.isSpy(actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(actual) + '.');
}
var count = actual.calls.count();
var pass = count === 1;
var message = function() {
var msg = 'Expected spy ' + actual.and.identity() + (pass ? ' not ' : ' ') +
'to have been called once, but ';
switch (count) {
case 0:
msg += 'it was never called.';
break;
case 1:
msg += 'it was called once.';
break;
default:
msg += 'it was called ' + count + ' times.';
break;
}
return msg;
};
return {
pass: pass,
message: message
};
}
};
return this.actual.callCount === 1 && this.env.contains_(this.actual.argsForCall, expectedArgs);
},
toHaveBeenCalledOnceWith: function(util, customEqualityTesters) {
return {
compare: generateCompare(false),
negativeCompare: generateCompare(true)
};
function generateCompare(isNot) {
return function(actual) {
if (!jasmine.isSpy(actual)) {
throw new Error('Expected a spy, but got ' + jasmine.pp(actual) + '.');
}
var expectedArgs = Array.prototype.slice.call(arguments, 1);
var actualCount = actual.calls.count();
var actualArgs = actualCount && actual.calls.argsFor(0);
var pass = (actualCount === 1) && util.equals(actualArgs, expectedArgs);
if (isNot) pass = !pass;
var message = function() {
var msg = 'Expected spy ' + actual.and.identity() + (isNot ? ' not ' : ' ') +
'to have been called once with ' + jasmine.pp(expectedArgs) + ', but ';
if (isNot) {
msg += 'it was.';
} else {
switch (actualCount) {
case 0:
msg += 'it was never called.';
break;
case 1:
msg += 'it was called with ' + jasmine.pp(actualArgs) + '.';
break;
default:
msg += 'it was called ' + actualCount + ' times.';
break;
}
}
return msg;
};
return {
pass: pass,
message: message
};
};
}
},
toBeOneOf: function() {
return Array.prototype.indexOf.call(arguments, this.actual) !== -1;
},
toHaveClass: function(clazz) {
this.message = function() {
return "Expected '" + angular.mock.dump(this.actual) + "'" + (this.isNot ? " not " : "") + " to have class '" + clazz + "'.";
};
var classes = clazz.trim().split(/\s+/);
for (var i = 0; i < classes.length; ++i) {
if (!jqLiteHasClass(this.actual[0], classes[i])) {
return false;
return {
compare: function(actual) {
var expectedArgs = Array.prototype.slice.call(arguments, 1);
return { pass: expectedArgs.indexOf(actual) !== -1 };
}
}
return true;
},
toThrowMatching: function(expected) {
return jasmine.Matchers.prototype.toThrow.call(this, expected);
},
toThrowMinErr: function(namespace, code, content) {
var result,
exception,
exceptionMessage = '',
escapeRegexp = function(str) {
// This function escapes all special regex characters.
// We use it to create matching regex from arbitrary strings.
// http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
codeRegex = new RegExp('^\\[' + escapeRegexp(namespace) + ':' + escapeRegexp(code) + '\\]'),
not = this.isNot ? "not " : "",
regex = jasmine.isA_("RegExp", content) ? content :
angular.isDefined(content) ? new RegExp(escapeRegexp(content)) : undefined;
if (!angular.isFunction(this.actual)) {
throw new Error('Actual is not a function');
}
try {
this.actual();
} catch (e) {
exception = e;
}
if (exception) {
exceptionMessage = exception.message || exception;
}
this.message = function() {
return "Expected function " + not + "to throw " +
namespace + "MinErr('" + code + "')" +
(regex ? " matching " + regex.toString() : "") +
(exception ? ", but it threw " + exceptionMessage : ".");
};
},
result = codeRegex.test(exceptionMessage);
if (!result) {
return result;
toHaveClass: function() {
return {
compare: generateCompare(false),
negativeCompare: generateCompare(true)
};
function generateCompare(isNot) {
return function(actual, clazz) {
var message = function() {
return "Expected '" + angular.mock.dump(actual) + "'" + (isNot ? " not " : "") + " to have class '" + clazz + "'.";
};
var classes = clazz.trim().split(/\s+/);
for (var i = 0; i < classes.length; ++i) {
if (!jqLiteHasClass(actual[0], classes[i])) {
return { pass: isNot };
}
}
return { pass: !isNot };
};
}
},
if (angular.isDefined(regex)) {
return regex.test(exceptionMessage);
toThrowMinErr: function() {
return {
compare: generateCompare(false),
negativeCompare: generateCompare(true)
};
function generateCompare(isNot) {
return function(actual, namespace, code, content) {
var result,
exception,
exceptionMessage = '',
escapeRegexp = function(str) {
// This function escapes all special regex characters.
// We use it to create matching regex from arbitrary strings.
// http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
codeRegex = new RegExp('^\\[' + escapeRegexp(namespace) + ':' + escapeRegexp(code) + '\\]'),
not = isNot ? "not " : "",
regex = jasmine.isA_("RegExp", content) ? content :
angular.isDefined(content) ? new RegExp(escapeRegexp(content)) : undefined;
if (!angular.isFunction(actual)) {
throw new Error('Actual is not a function');
}
try {
actual();
} catch (e) {
exception = e;
}
if (exception) {
exceptionMessage = exception.message || exception;
}
var message = function() {
return "Expected function " + not + "to throw " +
namespace + "MinErr('" + code + "')" +
(regex ? " matching " + regex.toString() : "") +
(exception ? ", but it threw " + exceptionMessage : ".");
};
result = codeRegex.test(exceptionMessage);
if (!result) {
if (isNot) {
return { pass: !result, message: message };
} else {
return { pass: result, message: message };
}
}
if (angular.isDefined(regex)) {
if (isNot) {
return { pass: !regex.test(exceptionMessage), message: message };
} else {
return { pass: regex.test(exceptionMessage), message: message };
}
}
if (isNot) {
return { pass: !result, message: message };
} else {
return { pass: result, message: message };
}
};
}
return result;
}
});
});
// TODO(vojta): remove this once Jasmine in Karma gets updated
// https://github.com/pivotal/jasmine/blob/c40b64a24c607596fa7488f2a0ddb98d063c872a/src/core/Matchers.js#L217-L246
// This toThrow supports RegExps.
jasmine.Matchers.prototype.toThrow = function(expected) {
var result = false;
var exception, exceptionMessage;
if (typeof this.actual != 'function') {
throw new Error('Actual is not a function');
}
try {
this.actual();
} catch (e) {
exception = e;
}
if (exception) {
exceptionMessage = exception.message || exception;
result = (isUndefined(expected) || this.env.equals_(exceptionMessage, expected.message || expected) || (jasmine.isA_("RegExp", expected) && expected.test(exceptionMessage)));
}
var not = this.isNot ? "not " : "";
var regexMatch = jasmine.isA_("RegExp", expected) ? " an exception matching" : "";
this.message = function() {
if (exception) {
return ["Expected function " + not + "to throw" + regexMatch, expected ? expected.message || expected : "an exception", ", but it threw", exceptionMessage].join(' ');
} else {
return "Expected function to throw an exception.";
}
};
return result;
};
/**
* Create jasmine.Spy on given method, but ignore calls without arguments
* This is helpful when need to spy only setter methods and ignore getters
*/
function spyOnlyCallsWithArgs(obj, method) {
var originalFn = obj[method];
var spy = spyOn(obj, method);
obj[method] = function() {
if (arguments.length) return spy.apply(this, arguments);
return spy.originalValue.apply(this);
return originalFn.apply(this);
};
return spy;
}
// Minimal implementation to mock what was removed from Jasmine 1.x
function createAsync(doneFn) {
function Job() {
this.next = [];
}
Job.prototype.done = function() {
return this.runs(doneFn);
};
Job.prototype.runs = function(fn) {
var newJob = new Job();
this.next.push(function() {
fn();
newJob.start();
});
return newJob;
};
Job.prototype.waitsFor = function(fn, error, timeout) {
var newJob = new Job();
timeout = timeout || 5000;
this.next.push(function() {
var counter = 0,
intervalId = window.setInterval(function() {
if (fn()) {
window.clearInterval(intervalId);
newJob.start();
}
counter += 5;
if (counter > timeout) {
window.clearInterval(intervalId);
throw new Error(error);
}
}, 5);
});
return newJob;
};
Job.prototype.waits = function(timeout) {
return this.waitsFor(function() { return true; }, undefined, timeout);
};
Job.prototype.start = function() {
var i;
for (i = 0; i < this.next.length; i += 1) {
this.next[i]();
}
};
return new Job();
}

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