` instead), or
4. if you **do use** custom element tags, then you must take these steps to make IE 8 and below happy:
-
+
```html
@@ -64,7 +64,7 @@ To make your Angular application work on IE please make sure that:
document.createElement('ng-include');
document.createElement('ng-pluralize');
document.createElement('ng-view');
-
+
// Optionally these for CSS
document.createElement('ng:include');
document.createElement('ng:pluralize');
@@ -79,7 +79,7 @@ To make your Angular application work on IE please make sure that:
```
5. Use `ng-style` tags instead of `style="{{ someCss }}"`. The later works in Chrome and Firefox
but does not work in Internet Explorer <= 11 (the most recent version at time of writing).
-
+
The **important** parts are:
@@ -165,7 +165,7 @@ In IE, the behavior is that the `BODY` element has three children:
## CSS Styling of Custom Tag Names
-To make CSS selectors work with custom elements, the custom element name must be pre-created with
+To make CSS selectors work with custom elements, the custom element name must be pre-created with
`document.createElement('my-tag')` regardless of XML namespace.
```html
diff --git a/docs/content/guide/migration.ngdoc b/docs/content/guide/migration.ngdoc
index 0695d2db4b6a..df042582b721 100644
--- a/docs/content/guide/migration.ngdoc
+++ b/docs/content/guide/migration.ngdoc
@@ -7,7 +7,7 @@ AngularJS version 1.2 introduces several breaking changes that may require chang
application's source code.
Although we try to avoid breaking changes, there are some cases where it is unavoidable.
-AngularJS 1.2 has undergone a thourough security review to make applications safer by default,
+AngularJS 1.2 has undergone a thorough security review to make applications safer by default,
which has driven many of these changes. Several new features, especially animations, would not
be possible without a few changes. Finally, some outstanding bugs were best fixed by changing
an existing API.
@@ -43,11 +43,12 @@ below should still apply, but you may want to consult the
{@link guide/migration#ngscenario ngScenario}
{@link guide/migration#nginclude-and-ngview-replace-its-entire-element-on-update ngInclude and ngView replace its entire element on update}
{@link guide/migration#urls-are-now-sanitized-against-a-whitelist URLs are now sanitized against a whitelist}
-
{@link guide/migration#isolate-scope-only-exposed-to-directives-with-property Isolate scope only exposed to directives with scope
property}
+
{@link guide/migration#isolate-scope-only-exposed-to-directives-with-scope-property Isolate scope only exposed to directives with scope
property}
{@link guide/migration#change-to-interpolation-priority Change to interpolation priority}
{@link guide/migration#underscore-prefixed/suffixed-properties-are-non-bindable Underscore-prefixed/suffixed properties are non-bindable}
{@link guide/migration#you-cannot-bind-to-select[multiple] You cannot bind to select[multiple]}
{@link guide/migration#uncommon-region-specific-local-files-were-removed-from-i18n Uncommon region-specific local files were removed from i18n}
+
{@link guide/migration#services-can-now-return-functions Services can now return functions}
@@ -493,7 +494,7 @@ See [31f190d4](https://github.com/angular/angular.js/commit/31f190d4d53921d32253
the priority of ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView has changed. This could affect directives that explicitly specify their priority.
-In order to make ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView work together in all common scenarios their directives are being adjusted to achieve the following precendence:
+In order to make ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView work together in all common scenarios their directives are being adjusted to achieve the following precedence:
Directive | Old Priority | New Priority
@@ -532,7 +533,7 @@ See [7d69d52a](https://github.com/angular/angular.js/commit/7d69d52acff8578e0f7d
A whitelist configured via `$compileProvider` can be used to configure what URLs are considered safe.
By default all common protocol prefixes are whitelisted including `data:` URIs with mime types `image/*`.
-This change sholdn't impact apps that don't contain malicious image links.
+This change shouldn't impact apps that don't contain malicious image links.
See [1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d61286840177607edd552a9df97),
[3e39ac7e](https://github.com/angular/angular.js/commit/3e39ac7e1b10d4812a44dad2f959a93361cd823b).
@@ -613,7 +614,7 @@ controller.) That's easier said that done for two reasons:
someone on the scope chain for JavaScript use, you also expose it to
Angular expressions
2. The new `controller as` syntax that's now in increased usage exposes the
-entire controller on the scope chain greatly increaing the exposed surface.
+entire controller on the scope chain greatly increasing the exposed surface.
Though Angular expressions are written and controlled by the developer, they:
@@ -653,3 +654,39 @@ load and use your copy of the locale file provided that you maintain it yourself
See [6382e21f](https://github.com/angular/angular.js/commit/6382e21fb28541a2484ac1a241d41cf9fbbe9d2c).
+## Services can now return functions
+
+Previously, the service constructor only returned objects regardless of whether a function was returned.
+
+Now, `$injector.instantiate` (and thus `$provide.service`) behaves the same as the native
+`new` operator and allows functions to be returned as a service.
+
+If using a JavaScript preprocessor it's quite possible when upgrading that services could start behaving incorrectly.
+Make sure your services return the correct type wanted.
+
+**Coffeescript example**
+
+```
+myApp.service 'applicationSrvc', ->
+ @something = "value"
+ @someFunct = ->
+ "something else"
+```
+
+pre 1.2 this service would return the whole object as the service.
+
+post 1.2 this service returns `someFunct` as the value of the service
+
+you would need to change this services to
+
+```
+myApp.service 'applicationSrvc', ->
+ @something = "value"
+ @someFunct = ->
+ "something else"
+ return
+```
+
+to continue to return the complete instance.
+
+See [c22adbf1](https://github.com/angular/angular.js/commit/c22adbf160f32c1839fbb35382b7a8c6bcec2927).
diff --git a/docs/content/guide/module.ngdoc b/docs/content/guide/module.ngdoc
index e2925584eacf..bfd9ca5f5816 100644
--- a/docs/content/guide/module.ngdoc
+++ b/docs/content/guide/module.ngdoc
@@ -66,8 +66,9 @@ that you break your application to multiple modules like this:
* And an application level module which depends on the above modules and contains any
initialization code.
-We've also written a document on how we organize large apps at Google and on how to write
-reusable components.
+We've also
+[written a document](http://blog.angularjs.org/2014/02/an-angularjs-style-guide-and-best.html)
+on how we organize large apps at Google.
The above is a suggestion. Tailor it to your needs.
@@ -172,7 +173,7 @@ angular.module('myModule', []).
When bootstrapping, first Angular applies all constant definitions.
-Then Angular applies configuration blocks in the order same order they were registered.
+Then Angular applies configuration blocks in the same order they were registered.
## Run Blocks
diff --git a/docs/content/guide/providers.ngdoc b/docs/content/guide/providers.ngdoc
index b62ba8d8303d..1297865edb65 100644
--- a/docs/content/guide/providers.ngdoc
+++ b/docs/content/guide/providers.ngdoc
@@ -143,7 +143,7 @@ primitive, object literal, function, or even an instance of a custom type.
## Service Recipe
JavaScript developers often use custom types to write object-oriented code. Let's explore how we
-could launch a unicorn into the space via our `unicornLauncher` service that is an instance of
+could launch a unicorn into space via our `unicornLauncher` service that is an instance of
custom type:
```javascript
diff --git a/docs/content/guide/services.ngdoc b/docs/content/guide/services.ngdoc
index d92be1426c59..bc2a4f85d143 100644
--- a/docs/content/guide/services.ngdoc
+++ b/docs/content/guide/services.ngdoc
@@ -11,15 +11,15 @@ Angular services are:
* Lazily instantiated – Angular only instantiates a service when an application component depends
on it.
-* Singletons – Each component is dependent on a service gets a reference to the single instance
+* Singletons – Each component dependent on a service gets a reference to the single instance
generated by the service factory.
-Angular offers several useful services (like {@link ng.$http `$http`}) but for most applications
+Angular offers several useful services (like {@link ng.$http `$http`}), but for most applications
you'll also want to {@link services#creating-services create your own}.
**Note:** Like other core Angular identifiers built-in services always start with `$`
-(i.e. `$http`).
+(e.g. `$http`).
@@ -129,7 +129,7 @@ injection of `$window`, `$scope`, and our `notify` service:
-
+
**Careful:** If you plan to [minify](http://en.wikipedia.org/wiki/Minification_(programming) your code,
your variable names will get renamed unless you use one of the annotation techniques above.
@@ -215,8 +215,8 @@ In the example, note that:
{@link ng.$log `$log`} services.
* The `routeTemplateMonitor` service depends on the built-in {@link ngRoute.$route `$route`}
service and our custom `batchLog` service.
-* Both services use the and array notation to declare their dependencies.
-* That the order of identifiers in the array is the same as the order of argument
+* Both services use the array notation to declare their dependencies.
+* The order of identifiers in the array is the same as the order of argument
names in the factory function.
### Registering a Service with `$provide`
@@ -234,7 +234,7 @@ angular.module('myModule', []).config(function($provide) {
});
```
-This is technique is often used in unit tests to mock out a service's dependencies.
+This technique is often used in unit tests to mock out a service's dependencies.
## Unit Testing
diff --git a/docs/content/guide/unit-testing.ngdoc b/docs/content/guide/unit-testing.ngdoc
index 8d41bedfb723..f4a608d8962d 100644
--- a/docs/content/guide/unit-testing.ngdoc
+++ b/docs/content/guide/unit-testing.ngdoc
@@ -258,7 +258,7 @@ expect($scope.strength).toEqual('weak');
```
Notice that the test is not only much shorter, it is also easier to follow what is happening. We say
-that such a test tells a story, rather then asserting random bits which don't seem to be related.
+that such a test tells a story, rather than asserting random bits which don't seem to be related.
## Filters
{@link ng.$filterProvider Filters} are functions which transform the data into a user readable
diff --git a/docs/content/misc/contribute.ngdoc b/docs/content/misc/contribute.ngdoc
index 9adeeee45a43..dd0a75f38983 100644
--- a/docs/content/misc/contribute.ngdoc
+++ b/docs/content/misc/contribute.ngdoc
@@ -11,12 +11,12 @@ See the [contributing guidelines](https://github.com/angular/angular.js/blob/mas
for how to contribute your own code to AngularJS.
-1. {@link #building-and-testing-angularjs_installing-dependencies Installing Dependencies}
-2. {@link #building-and-testing-angularjs_forking-angular-on-github Forking Angular on Github}
-3. {@link #building-and-testing-angularjs_building-angularjs Building AngularJS}
-4. {@link #building-and-testing-angularjs_running-a-local-development-web-server Running a Local Development Web Server}
-5. {@link #building-and-testing-angularjs_running-the-unit-test-suite Running the Unit Test Suite}
-6. {@link #building-and-testing-angularjs_running-the-end-to-end-test-suite Running the End-to-end Test Suite}
+1. {@link misc/contribute#installing-dependencies Installing Dependencies}
+2. {@link misc/contribute#forking-angular-on-github Forking Angular on Github}
+3. {@link misc/contribute#building-angularjs Building AngularJS}
+4. {@link misc/contribute#running-a-local-development-web-server Running a Local Development Web Server}
+5. {@link misc/contribute#running-the-unit-test-suite Running the Unit Test Suite}
+6. {@link misc/contribute#running-the-end-to-end-test-suite Running the End-to-end Test Suite}
## Installing Dependencies
diff --git a/docs/content/tutorial/index.ngdoc b/docs/content/tutorial/index.ngdoc
index dfceb03ba1d1..67a1f50161e5 100644
--- a/docs/content/tutorial/index.ngdoc
+++ b/docs/content/tutorial/index.ngdoc
@@ -9,7 +9,8 @@ the construction of an AngularJS web app. The app you will build is a catalog th
of Android devices, lets you filter the list to see only devices that interest you, and then view
details for any device.
-
+
Work through the tutorial to see how Angular makes browsers smarter — without the use of extensions
or plug-ins. As you work through the tutorial, you will:
@@ -57,63 +58,71 @@ and follow the instructions for setting up your computer.
You'll need Git, which you can get from
- the Git site .
+ the Git site .
Clone the angular-phonecat repository located at
- Github by running the following command:
+ Github by
+ running the following command:
git clone https://github.com/angular/angular-phonecat.git
- This command creates the angular-phonecat
directory in your current
-directory.
+ This command creates the angular-phonecat
directory in your current directory.
Change your current directory to angular-phonecat
:
cd angular-phonecat
- The tutorial instructions, from now on, assume you are running all commands from the angular-phonecat
-directory.
+ The tutorial instructions, from now on, assume you are running all commands from the
+ angular-phonecat
directory.
You will also need Node.js and Karma to run unit tests, so please verify that you have
- Node.js v0.10 or better installed
+ Node.js v0.10 or better installed
and that the node
executable is on your PATH
by running the following
command in a terminal window:
node --version
- Additionally install Karma and its plugins if you
- don't have it already:
+ **Helpful note:** If you need to run a different version of
+ node.js in your local environment, consider installing
+
+ Node Version Manager (nvm) .
+ Additionally install Karma and
+ its plugins if you don't have it already:
npm install
You will need an http server running on your system. Mac and Linux machines typically
-have Apache pre-installed, but If you don't already have one installed, you can use node
-to run a simple bundled http server: node scripts/web-server.js
.
+ have Apache pre-installed, but If you don't already have one installed, you can use node
+ to run scripts/web-server.js
, a simple bundled http server.
You will need Node.js and Karma to run unit tests, so please verify that you have
- Node.js v0.10 or better installed
+ Node.js v0.10 or better installed
and that the node
executable is on your PATH
by running the following
command in a terminal window:
node --version
- Additionally install Karma if you
- don't have it already:
+ **Helpful note:** If you need to run a different version of
+ node.js in your local environment, consider installing
+
+ Node Version Manager (nvm) .
+ Additionally install Karma
+ if you don't have it already:
npm install -g karma
You'll also need Git, which you can get from
- the Git site .
+ the Git site .
Clone the angular-phonecat repository located at Github by running the following command:
- git clone https://github.com/angular/angular-phonecat.git
+ href="https://github.com/angular/angular-phonecat" "Github Angular-phonecat Repo">Github by running
+ the following command:git clone https://github.com/angular/angular-phonecat.git
This command creates the angular-phonecat
directory in your current directory.
Change your current directory to angular-phonecat
:
cd angular-phonecat
The tutorial instructions assume you are running all commands from the angular-phonecat
-directory.
+ directory.
You should run all git
commands from Git bash.
- Other commands like test.bat
or e2e-test.bat
should be
-executed from the Windows command line.
- You need an http server running on your system, but if you don't already have one
-already installed, you can use node
to run a simple
-bundled http server: node scripts\web-server.js
.
+ Other commands like test.bat
or e2e-test.bat
should be executed from the
+ Windows command line.
+
You need an http server running on your system, but if you don't already have one already
+ installed, you can use node
to run scripts\web-server.js
, a simple bundled
+ http server.
-The last thing to do is to make sure your computer has a web browser and a good text editor
-installed. Now, let's get some cool stuff done!
+The last thing to do is to make sure your computer has a web browser and a good text editor installed. Now,
+let's get some cool stuff done!
-{@link step_00
Get Started! }
+
Get Started!
diff --git a/docs/content/tutorial/step_02.ngdoc b/docs/content/tutorial/step_02.ngdoc
index 37e91ef74716..6ea936a90e26 100644
--- a/docs/content/tutorial/step_02.ngdoc
+++ b/docs/content/tutorial/step_02.ngdoc
@@ -183,7 +183,7 @@ is available to be injected.
### Writing and Running Tests
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
writing tests. Although Angular does not require you to use Jasmine, we wrote all of the tests in
-this tutorial in Jasmine. You can learn about Jasmine on the [Jasmine home page](http://pivotal.github.com/jasmine/) and at the [Jasmine docs](http://pivotal.github.io/jasmine/).
+this tutorial in Jasmine. You can learn about Jasmine on the [Jasmine home page](http://jasmine.github.io/) and at the [Jasmine docs](http://jasmine.github.io/).
The angular-seed project is pre-configured to run all unit tests using [Karma](http://karma-runner.github.io/). Ensure that the necessary karma plugins are installed.
You can do this by issuing `npm install` into your terminal.
@@ -226,6 +226,10 @@ To run the test, do the following:
Refresh your browser and verify that it says "Hello, World!".
+* Update the unit test for the controler in ./tests/unit/controlersSpec.js to reflect the previous change. For example by adding:
+
+ expect(scope.name).toBe('World');
+
* Create a repeater that constructs a simple table:
diff --git a/docs/content/tutorial/step_04.ngdoc b/docs/content/tutorial/step_04.ngdoc
index cffc6319ad4a..48b19f7a1dd0 100644
--- a/docs/content/tutorial/step_04.ngdoc
+++ b/docs/content/tutorial/step_04.ngdoc
@@ -91,7 +91,7 @@ phonecatApp.controller('PhoneListCtrl', function ($scope) {
record. This property is used to order phones by age.
* We added a line to the controller that sets the default value of `orderProp` to `age`. If we had
-not set a default value here, the `orderBy` filter would remain uninitialized until our
+not set a default value here, the `orderBy` filter would remain uninitialized until our
user picked an option from the drop down menu.
This is a good time to talk about two-way data-binding. Notice that when the app is loaded in the
@@ -117,7 +117,7 @@ describe('PhoneCat controllers', function() {
var scope, ctrl;
beforeEach(module('phonecatApp'));
-
+
beforeEach(inject(function($controller) {
scope = {};
ctrl = $controller('PhoneListCtrl', {$scope:scope});
diff --git a/docs/content/tutorial/step_05.ngdoc b/docs/content/tutorial/step_05.ngdoc
index 8291eeb85992..031d97b751d8 100644
--- a/docs/content/tutorial/step_05.ngdoc
+++ b/docs/content/tutorial/step_05.ngdoc
@@ -7,7 +7,7 @@
Enough of building an app with three phones in a hard-coded dataset! Let's fetch a larger dataset
-from our server using one of Angular's built-in {@link guide/dev_guide.services services} called {@link
+from our server using one of Angular's built-in {@link guide/services services} called {@link
ng.$http $http}. We will use Angular's {@link guide/di dependency
injection (DI)} to provide the service to the `PhoneListCtrl` controller.
@@ -20,7 +20,6 @@ You should now see a list of 20 phones.
The most important changes are listed below. You can see the full diff on [GitHub](https://github.com/angular/angular-phonecat/compare/step-4...step-5):
## Data
-
The `app/phones/phones.json` file in your project is a dataset that contains a larger list of phones
stored in the JSON format.
@@ -44,7 +43,7 @@ Following is a sample of the file:
We'll use Angular's {@link ng.$http $http} service in our controller to make an HTTP
request to your web server to fetch the data in the `app/phones/phones.json` file. `$http` is just
-one of several built-in {@link guide/dev_guide.services angular services} that handle common operations
+one of several built-in {@link guide/services Angular services} that handle common operations
in web apps. Angular injects these services for you where you need them.
Services are managed by Angular's {@link guide/di DI subsystem}. Dependency injection
@@ -74,10 +73,10 @@ tutorial.)
The `$http` service returns a {@link ng.$q promise object} with a `success`
method. We call this method to handle the asynchronous response and assign the phone data to the
-scope controlled by this controller, as a model called `phones`. Notice that angular detected the
+scope controlled by this controller, as a model called `phones`. Notice that Angular detected the
json response and parsed it for us!
-To use a service in angular, you simply declare the names of the dependencies you need as arguments
+To use a service in Angular, you simply declare the names of the dependencies you need as arguments
to the controller's constructor function, as follows:
phonecatApp.controller('PhoneListCtrl', function ($scope, $http) {...}
@@ -96,7 +95,7 @@ dependencies.
### `$` Prefix Naming Convention
You can create your own services, and in fact we will do exactly that in step 11. As a naming
-convention, angular's built-in services, Scope methods and a few other Angular APIs have a `$`
+convention, Angular's built-in services, Scope methods and a few other Angular APIs have a `$`
prefix in front of the name.
The `$` prefix is there to namespace Angular-provided services.
@@ -167,7 +166,7 @@ __`test/unit/controllersSpec.js`:__
Because we started using dependency injection and our controller has dependencies, constructing the
controller in our tests is a bit more complicated. We could use the `new` operator and provide the
constructor with some kind of fake `$http` implementation. However, the recommended (and easier) way
-is to create a controller in the test environment in the same way that angular does it in the
+is to create a controller in the test environment in the same way that Angular does it in the
production code behind the scenes, as follows:
```js
@@ -269,7 +268,7 @@ to the first 5 in the list. Use the following code in the `$http` callback:
# Summary
-Now that you have learned how easy it is to use angular services (thanks to Angular's dependency
+Now that you have learned how easy it is to use Angular services (thanks to Angular's dependency
injection), go to {@link step_06 step 6}, where you will add some
thumbnail images of phones and some links.
diff --git a/docs/content/tutorial/step_06.ngdoc b/docs/content/tutorial/step_06.ngdoc
index 04c19f663fc6..61586c79e4ca 100644
--- a/docs/content/tutorial/step_06.ngdoc
+++ b/docs/content/tutorial/step_06.ngdoc
@@ -63,7 +63,7 @@ the element attribute.
We also added phone images next to each record using an image tag with the {@link
ng.directive:ngSrc ngSrc} directive. That directive prevents the
-browser from treating the angular `{{ expression }}` markup literally, and initiating a request to
+browser from treating the Angular `{{ expression }}` markup literally, and initiating a request to
invalid url `http://localhost:8000/app/{{phone.imageUrl}}`, which it would have done if we had only
specified an attribute binding in a regular `src` attribute (` `).
Using the `ngSrc` directive prevents the browser from making an http request to an invalid location.
diff --git a/docs/content/tutorial/step_07.ngdoc b/docs/content/tutorial/step_07.ngdoc
index 4c45f2d4b273..f6fdac5c0a51 100644
--- a/docs/content/tutorial/step_07.ngdoc
+++ b/docs/content/tutorial/step_07.ngdoc
@@ -114,7 +114,7 @@ Our application routes are defined as follows:
view, Angular will use the `phone-list.html` template and the `PhoneListCtrl` controller.
* The phone details view will be shown when the URL hash fragment matches '/phone/:phoneId', where
-`:phoneId` is a variable part of the URL. To construct the phone details view, angular will use the
+`:phoneId` is a variable part of the URL. To construct the phone details view, Angular will use the
`phone-detail.html` template and the `PhoneDetailCtrl` controller.
We reused the `PhoneListCtrl` controller that we constructed in previous steps and we added a new,
diff --git a/docs/content/tutorial/step_08.ngdoc b/docs/content/tutorial/step_08.ngdoc
index 25de2cb8b8cf..f432df74c512 100644
--- a/docs/content/tutorial/step_08.ngdoc
+++ b/docs/content/tutorial/step_08.ngdoc
@@ -79,7 +79,7 @@ route by the `$route` service.
## Template
The TBD placeholder line has been replaced with lists and bindings that comprise the phone details.
-Note where we use the angular `{{expression}}` markup and `ngRepeat` to project phone data from
+Note where we use the Angular `{{expression}}` markup and `ngRepeat` to project phone data from
our model into the view.
@@ -107,7 +107,7 @@ __`app/partials/phone-detail.html`:__
...
-
+
Additional Features
{{phone.additionalFeatures}}
diff --git a/docs/content/tutorial/step_09.ngdoc b/docs/content/tutorial/step_09.ngdoc
index e95025fbfa2a..a3c6c6adabcf 100644
--- a/docs/content/tutorial/step_09.ngdoc
+++ b/docs/content/tutorial/step_09.ngdoc
@@ -40,13 +40,13 @@ The name of our filter is "checkmark". The `input` evaluates to either `true` or
return one of the two unicode characters we have chosen to represent true (`\u2713` -> ✓) or false (`\u2718` -> ✘).
Now that our filter is ready, we need to register the `phonecatFilters` module as a dependency for
-our main `phonecat` module.
+our main `phonecatApp` module.
__`app/js/app.js`:__
```js
...
-angular.module('phonecatApp', ['phonecatFilters']).
+angular.module('phonecatApp', ['ngRoute','phonecatControllers','phonecatFilters']).
...
```
diff --git a/docs/content/tutorial/step_11.ngdoc b/docs/content/tutorial/step_11.ngdoc
index 252795c11004..e85092c2580c 100644
--- a/docs/content/tutorial/step_11.ngdoc
+++ b/docs/content/tutorial/step_11.ngdoc
@@ -22,8 +22,8 @@ The most important changes are listed below. You can see the full diff on [GitHu
## Template
The custom service is defined in `app/js/services.js` so we need to include this file in our layout
-template. Additionally, we also need to load the `angular-resource.js` file, which contains the
-{@link api/ngResource ngResource} module and in it the {@link api/ngResource.$resource $resource}
+template. Additionally, we also need to load the `angular-resource.js` file, which contains the
+{@link api/ngResource ngResource} module and in it the {@link api/ngResource.$resource $resource}
service, that we'll soon use:
__`app/index.html`.__
diff --git a/docs/content/tutorial/step_12.ngdoc b/docs/content/tutorial/step_12.ngdoc
index 3e823a1064dc..060c7d0d3f2a 100644
--- a/docs/content/tutorial/step_12.ngdoc
+++ b/docs/content/tutorial/step_12.ngdoc
@@ -20,7 +20,8 @@ a dependency with the application module, will enable animations throughout the
Common `ng` directives automatically trigger hooks for animations to tap into. When an animation is found
then the animation will run in between the standard DOM operation that is being issued on the element at
-the given time (e.g. inserting and removing nodes on ngRepeat or adding and removing classes on ngClass).
+the given time (e.g. inserting and removing nodes on {@link api/ng.directive:ngRepeat `ngRepeat`} or adding
+and removing classes on {@link api/ng.directive:ngClass `ngClass`}).
The most important changes are listed below. You can see the full diff on
[GitHub](https://github.com/angular/angular-phonecat/compare/step-11...step-12):
@@ -34,9 +35,10 @@ To get an idea of how animations work with AngularJS, please read the
## Template
-The changes required within the HTML template code is to link the asset files which define the animations as well
-as the `angular-animate.js` file. The animation module, known as `ngAnimate`, is defined within
-`angular-animate.js` and contains the code necessary to make your application become animation aware.
+The changes required within the HTML template code is to link the asset files which define the animations as
+well as the `angular-animate.js` file. The animation module, known as {@link api/ngAnimate `ngAnimate`}, is
+defined within `angular-animate.js` and contains the code necessary to make your application become animation
+aware.
Here's what needs to changed in the index file:
@@ -83,7 +85,7 @@ __`app/js/app.js`.__
```js
// ...
-angular.module('phonecat', [
+angular.module('phonecatApp', [
'ngRoute',
'phonecatAnimations',
@@ -197,7 +199,7 @@ which are described in detail below.
## Animating `ngView` with CSS Keyframe Animations
-Next let's add an animation for transitions between route changes in `ngView`.
+Next let's add an animation for transitions between route changes in {@link api/ngRoute.directive:ngView `ngView`}.
To start, let's add a new CSS class to our HTML like we did in the example above.
This time, instead of the `ng-repeat` element, let's add it to the element containing the ng-view directive.
diff --git a/docs/docs.config.js b/docs/docs.config.js
index c1539cb64152..fcd3f9d9ffc7 100644
--- a/docs/docs.config.js
+++ b/docs/docs.config.js
@@ -1,13 +1,12 @@
var path = require('canonical-path');
-var gruntUtils = require('../lib/grunt/utils');
+var versionInfo = require('../lib/versions/version-info');
var basePath = __dirname;
var basePackage = require('./config');
module.exports = function(config) {
- var version = gruntUtils.getVersion();
- var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + version.cdn;
+ var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + versionInfo.currentPackage.cdnVersion;
var getVersion = function(component, sourceFolder, packageFile) {
sourceFolder = sourceFolder || '../bower_components';
@@ -25,9 +24,12 @@ module.exports = function(config) {
{ pattern: '**/*.ngdoc', basePath: path.resolve(basePath, 'content') }
]);
+ config.set('processing.stopOnError', true);
+
config.set('processing.errors.minerrInfoPath', path.resolve(basePath, '../build/errors.json'));
config.set('rendering.outputFolder', '../build/docs');
+ config.set('rendering.contentsFolder', 'partials');
config.set('logging.level', 'info');
@@ -38,7 +40,7 @@ module.exports = function(config) {
commonFiles: {
scripts: [ '../../../angular.js' ]
},
- dependencyPath: '../../..'
+ dependencyPath: '../../../'
},
scripts: [
'../angular.js',
@@ -73,7 +75,7 @@ module.exports = function(config) {
commonFiles: {
scripts: [ '../../../angular.min.js' ]
},
- dependencyPath: '../../..'
+ dependencyPath: '../../../'
},
scripts: [
'../angular.min.js',
@@ -111,7 +113,7 @@ module.exports = function(config) {
'../../../angular.js'
]
},
- dependencyPath: '../../..'
+ dependencyPath: '../../../'
},
scripts: [
'components/jquery-' + getVersion('jquery') + '/jquery.js',
@@ -147,7 +149,7 @@ module.exports = function(config) {
commonFiles: {
scripts: [ cdnUrl + '/angular.min.js' ]
},
- dependencyPath: cdnUrl
+ dependencyPath: cdnUrl + '/'
},
scripts: [
cdnUrl + '/angular.min.js',
diff --git a/docs/gulpfile.js b/docs/gulpfile.js
index caef1a89e414..c47c04f5b606 100644
--- a/docs/gulpfile.js
+++ b/docs/gulpfile.js
@@ -49,7 +49,11 @@ gulp.task('assets', ['bower'], function() {
gulp.task('doc-gen', function() {
- return docGenerator('docs.config.js').generateDocs();
+ return docGenerator('docs.config.js')
+ .generateDocs()
+ .catch(function(error) {
+ process.exit(1);
+ });
});
// JSHint the example and protractor test files
diff --git a/i18n/spec/converterSpec.js b/i18n/spec/converterSpec.js
index a7879e6beacf..e02bde9f4155 100644
--- a/i18n/spec/converterSpec.js
+++ b/i18n/spec/converterSpec.js
@@ -33,7 +33,7 @@ describe("convertDatetimeData", function() {
AMPMS: ['AM', 'PM'],
DATEFORMATS: ['a', 'b', 'c', 'd'],
TIMEFORMATS: ['e', 'f', 'g', 'h'] };
-
+
it('should convert empty datetime obj', function() {
var processedData = convert(dataObj);
expect(processedData.MONTH).toEqual(['Enero', 'Pebrero']);
diff --git a/jenkins_build.sh b/jenkins_build.sh
index 806eb0f2c95f..b6800082e21f 100755
--- a/jenkins_build.sh
+++ b/jenkins_build.sh
@@ -10,7 +10,7 @@ set -xe
# Define reasonable set of browsers in case we are running manually from commandline
if [[ -z "$BROWSERS" ]]
then
- BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh,/Users/jenkins/bin/ie8.sh,/Users/jenkins/bin/ie9.sh"
+ BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh"
fi
# CLEAN #
diff --git a/lib/grunt/utils.js b/lib/grunt/utils.js
index b9affd822601..c00147fe8dac 100644
--- a/lib/grunt/utils.js
+++ b/lib/grunt/utils.js
@@ -4,8 +4,9 @@ var shell = require('shelljs');
var grunt = require('grunt');
var spawn = require('child_process').spawn;
var semver = require('semver');
+
var _ = require('lodash');
-var version, pkg;
+
var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n';
var PORT_MIN = 8000;
@@ -23,23 +24,6 @@ var getRandomPorts = function() {
];
};
-var getPackage = function() {
- if ( !pkg ) {
-
- // Search up the folder hierarchy for the first package.json
- var packageFolder = path.resolve('.');
- while ( !fs.existsSync(path.join(packageFolder, 'package.json')) ) {
- var parent = path.dirname(packageFolder);
- if ( parent === packageFolder) { break; }
- packageFolder = parent;
- }
- pkg = JSON.parse(fs.readFileSync(path.join(packageFolder,'package.json'), 'UTF-8'));
-
- }
-
- return pkg;
-};
-
module.exports = {
@@ -50,160 +34,6 @@ module.exports = {
},
- getGitRepoInfo: function() {
- var GITURL_REGEX = /^https:\/\/github.com\/([^\/]+)\/(.+).git$/;
- var match = GITURL_REGEX.exec(getPackage().repository.url);
- var git = {
- owner: match[1],
- repo: match[2]
- };
- return git;
- },
-
-
- getVersion: function(){
- if (version) return version;
-
- try {
-
- var gitTag = getTagOfCurrentCommit();
- var semVerVersion, codeName, fullVersion;
- if (gitTag) {
- // tagged release
- fullVersion = semVerVersion = semver.valid(gitTag);
- codeName = getTaggedReleaseCodeName(gitTag);
- } else {
- // snapshot release
- semVerVersion = getSnapshotVersion();
- fullVersion = semVerVersion + '-' + getSnapshotSuffix();
- codeName = 'snapshot';
- }
-
- var versionParts = semVerVersion.match(/(\d+)\.(\d+)\.(\d+)/);
-
- version = {
- full: fullVersion,
- major: versionParts[1],
- minor: versionParts[2],
- dot: versionParts[3],
- codename: codeName,
- cdn: getPackage().cdnVersion
- };
-
- // Stable versions have an even minor version
- version.isStable = version.minor%2 === 0;
-
- return version;
-
- } catch (e) {
- grunt.fail.warn(e);
- }
-
- function getTagOfCurrentCommit() {
- var gitTagResult = shell.exec('git describe --exact-match', {silent:true});
- var gitTagOutput = gitTagResult.output.trim();
- var branchVersionPattern = new RegExp(getPackage().branchVersion.replace('.', '\\.').replace('*', '\\d+'));
- if (gitTagResult.code === 0 && gitTagOutput.match(branchVersionPattern)) {
- return gitTagOutput;
- } else {
- return null;
- }
- }
-
- function getTaggedReleaseCodeName(tagName) {
- var tagMessage = shell.exec('git cat-file -p '+ tagName +' | grep "codename"', {silent:true}).output;
- var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
- if (!codeName) {
- throw new Error("Could not extract release code name. The message of tag "+tagName+
- " must match '*codename(some release name)*'");
- }
- return codeName;
- }
-
- function getSnapshotVersion() {
- var oldTags = shell.exec('git tag -l v'+getPackage().branchVersion, {silent:true}).output.trim().split('\n');
- // ignore non semver versions.
- oldTags = oldTags.filter(function(version) {
- return version && semver.valid(version);
- });
- if (oldTags.length) {
- oldTags.sort(semver.compare);
- semVerVersion = oldTags[oldTags.length-1];
- if (semVerVersion.indexOf('-') !== -1) {
- semVerVersion = semver.inc(semVerVersion, 'prerelease');
- } else {
- semVerVersion = semver.inc(semVerVersion, 'patch');
- }
- } else {
- semVerVersion = semver.valid(getPackage().branchVersion.replace(/\*/g, '0'));
- }
- return semVerVersion;
- }
-
- function getSnapshotSuffix() {
- var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER || 'local';
- var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
- return 'build.'+jenkinsBuild+'+sha.'+hash;
- }
- },
-
- getPreviousVersions: function() {
- var VERSION_REGEX = /([1-9]\d*)\.(\d+)\.(\d+)(?:-?rc\.?(\d+)|-(snapshot))?/;
-
- // Pad out a number with zeros at the front to make it `digits` characters long
- function pad(num, digits) {
- var zeros = Array(digits+1).join('0');
- return (zeros+num).slice(-digits);
- }
-
- function padVersion(version) {
- // We pad out the version numbers with 0s so they sort nicely
- // - Non-Release Candidates get 9999 for their release candidate section to make them appear earlier
- // - Snapshots get 9 added to the front to move them to the top of the list
- var maxLength = 4;
- var padded = (version.snapshot ? '9' : '0') + pad(version.major, maxLength) +
- pad(version.minor, maxLength) + pad(version.dot, maxLength) +
- pad(version.rc || 9999, maxLength);
- return padded;
- }
-
- function getVersionFromTag(tag) {
- var match = VERSION_REGEX.exec(tag);
- if ( match ) {
- var version = {
- tag: tag,
- major: match[1], minor: match[2], dot: match[3], rc: match[4],
- snapshot: !!match[5] && getSnapshotSuffix()
- };
-
- if(version.snapshot) {
- version.full = version.major + '.' + version.minor + '.x (edge)';
- } else {
- version.full = version.major + '.' + version.minor + '.' + version.dot +
- (version.rc ? '-rc.' + version.rc : '');
- }
-
- // Stable versions have an even minor version and are not a release candidate
- version.isStable = !(version.minor%2 || version.rc);
-
- // Versions before 1.0.2 had a different docs folder name
- version.docsUrl = 'http://code.angularjs.org/' + version.full + '/docs';
- if ( version.major < 1 || (version.major === 1 && version.minor === 0 && version.dot < 2 ) ) {
- version.docsUrl += '-' + version.full;
- }
-
- return version;
- }
- }
-
- var tags = shell.exec('git tag', {silent: true}).output.split(/\s*\n\s*/);
- return _(tags)
- .map(getVersionFromTag)
- .filter() // getVersion can map to undefined - this clears those out
- .sortBy(padVersion)
- .value();
- },
-
startKarma: function(config, singleRun, done){
var browsers = grunt.option('browsers');
var reporters = grunt.option('reporters');
@@ -225,7 +55,7 @@ module.exports = {
},
- updateWebdriver: function(done){
+ updateWebdriver: function(done){
if (process.env.TRAVIS) {
// Skip the webdriver-manager update on Travis, since the browsers will
// be provided remotely.
@@ -319,9 +149,9 @@ module.exports = {
.replace(/"NG_VERSION_FULL"/g, NG_VERSION.full)
.replace(/"NG_VERSION_MAJOR"/, NG_VERSION.major)
.replace(/"NG_VERSION_MINOR"/, NG_VERSION.minor)
- .replace(/"NG_VERSION_DOT"/, NG_VERSION.dot)
+ .replace(/"NG_VERSION_DOT"/, NG_VERSION.patch)
.replace(/"NG_VERSION_CDN"/, NG_VERSION.cdn)
- .replace(/"NG_VERSION_CODENAME"/, NG_VERSION.codename);
+ .replace(/"NG_VERSION_CODENAME"/, NG_VERSION.codeName);
if (strict !== false) processed = this.singleStrict(processed, '\n\n', true);
return processed;
},
@@ -374,7 +204,7 @@ module.exports = {
var mapFile = minFile + '.map';
var mapFileName = mapFile.match(/[^\/]+$/)[0];
var errorFileName = file.replace(/\.js$/, '-errors.json');
- var versionNumber = this.getVersion().full;
+ var versionNumber = grunt.config('NG_VERSION').full;
shell.exec(
'java ' +
this.java32flags() + ' ' +
diff --git a/lib/versions/version-info.js b/lib/versions/version-info.js
new file mode 100644
index 000000000000..3ee5e261549e
--- /dev/null
+++ b/lib/versions/version-info.js
@@ -0,0 +1,182 @@
+var fs = require('fs');
+var path = require('path');
+var shell = require('shelljs');
+var semver = require('semver');
+var _ = require('lodash');
+
+var currentPackage, previousVersions;
+
+
+/**
+ * Load information about this project from the package.json
+ * @return {Object} The package information
+ */
+var getPackage = function() {
+ // Search up the folder hierarchy for the first package.json
+ var packageFolder = path.resolve('.');
+ while ( !fs.existsSync(path.join(packageFolder, 'package.json')) ) {
+ var parent = path.dirname(packageFolder);
+ if ( parent === packageFolder) { break; }
+ packageFolder = parent;
+ }
+ return JSON.parse(fs.readFileSync(path.join(packageFolder,'package.json'), 'UTF-8'));
+};
+
+
+/**
+ * Parse the github URL for useful information
+ * @return {Object} An object containing the github owner and repository name
+ */
+var getGitRepoInfo = function() {
+ var GITURL_REGEX = /^https:\/\/github.com\/([^\/]+)\/(.+).git$/;
+ var match = GITURL_REGEX.exec(currentPackage.repository.url);
+ var git = {
+ owner: match[1],
+ repo: match[2]
+ };
+ return git;
+};
+
+
+
+/**
+ * Extract the code name from the tagged commit's message - it should contain the text of the form:
+ * "codename(some-code-name)"
+ * @param {String} tagName Name of the tag to look in for the codename
+ * @return {String} The codename if found, otherwise null/undefined
+ */
+var getCodeName = function(tagName) {
+ var tagMessage = shell.exec('git cat-file -p '+ tagName +' | grep "codename"', {silent:true}).output;
+ var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
+ if (!codeName) {
+ throw new Error("Could not extract release code name. The message of tag "+tagName+
+ " must match '*codename(some release name)*'");
+ }
+ return codeName;
+};
+
+
+/**
+ * Compute a build segment for the version, from the Jenkins build number and current commit SHA
+ * @return {String} The build segment of the version
+ */
+function getBuild() {
+ var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
+ return 'sha.'+hash;
+}
+
+
+/**
+ * If the current commit is tagged as a version get that version
+ * @return {SemVer} The version or null
+ */
+var getTaggedVersion = function() {
+ var gitTagResult = shell.exec('git describe --exact-match', {silent:true});
+
+ if ( gitTagResult.code === 0 ) {
+ var tag = gitTagResult.output.trim();
+ var version = semver.parse(tag);
+
+ if ( version && semver.satisfies(version, currentPackage.branchVersion)) {
+ version.codeName = getCodeName(tag);
+ version.full = version.version;
+ return version;
+ }
+ }
+
+ return null;
+};
+
+/**
+ * Stable versions have an even minor version and have no prerelease
+ * @param {SemVer} version The version to test
+ * @return {Boolean} True if the version is stable
+ */
+var isStable = function(version) {
+ return semver.satisfies(version, '1.0 || 1.2') && version.prerelease.length === 0;
+};
+
+/**
+ * Get a collection of all the previous versions sorted by semantic version
+ * @return {Array.} The collection of previous versions
+ */
+var getPreviousVersions = function() {
+ // always use the remote tags as the local clone might
+ // not contain all commits when cloned with git clone --depth=...
+ // Needed e.g. for Travis
+ var repo_url = currentPackage.repository.url;
+ var tagResults = shell.exec('git ls-remote --tags ' + repo_url + ' | grep -o -e "v[0-9].*[0-9]$"',
+ {silent: true});
+ if ( tagResults.code === 0 ) {
+ return _(tagResults.output.trim().split('\n'))
+ .map(function(tag) {
+ var version = semver.parse(tag);
+ return version;
+ })
+ .filter()
+ .map(function(version) {
+ version.isStable = isStable(version);
+
+ version.docsUrl = 'http://code.angularjs.org/' + version.version + '/docs';
+ // Versions before 1.0.2 had a different docs folder name
+ if ( version.major < 1 || (version.major === 1 && version.minor === 0 && version.dot < 2 ) ) {
+ version.docsUrl += '-' + version.version;
+ }
+ return version;
+ })
+ .sort(semver.compare)
+ .value();
+ } else {
+ return [];
+ }
+};
+
+
+/**
+ * Get the unstable snapshot version
+ * @return {SemVer} The snapshot version
+ */
+var getSnapshotVersion = function() {
+ version = _(previousVersions)
+ .filter(function(tag) {
+ return semver.satisfies(tag, currentPackage.branchVersion);
+ })
+ .last();
+
+ if ( !version ) {
+ // a snapshot version before the first tag on the branch
+ version = semver(currentPackage.branchVersion.replace('*','0-beta.1'));
+ }
+
+ // We need to clone to ensure that we are not modifying another version
+ version = semver(version.raw);
+
+ var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER;
+ if (!version.prerelease || !version.prerelease.length) {
+ // last release was a non beta release. Increment the patch level to
+ // indicate the next release that we will be doing.
+ // E.g. last release was 1.3.0, then the snapshot will be
+ // 1.3.1-build.1, which is lesser than 1.3.1 accorind the semver!
+
+ // If the last release was a beta release we don't update the
+ // beta number by purpose, as otherwise the semver comparison
+ // does not work any more when the next beta is released.
+ // E.g. don't generate 1.3.0-beta.2.build.1
+ // as this is bigger than 1.3.0-beta.2 according to semver
+ version.patch++;
+ }
+ version.prerelease = jenkinsBuild ? ['build', jenkinsBuild] : ['local'];
+ version.build = getBuild();
+ version.codeName = 'snapshot';
+ version.isSnapshot = true;
+ version.format();
+ version.full = version.version + '+' + version.build;
+
+ return version;
+};
+
+
+exports.currentPackage = currentPackage = getPackage();
+exports.gitRepoInfo = gitRepoInfo = getGitRepoInfo();
+exports.previousVersions = previousVersions = getPreviousVersions();
+exports.currentVersion = getTaggedVersion() || getSnapshotVersion();
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
new file mode 100644
index 000000000000..726784e87224
--- /dev/null
+++ b/npm-shrinkwrap.json
@@ -0,0 +1,2808 @@
+{
+ "dependencies": {
+ "bower": {
+ "version": "1.2.8",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.4"
+ },
+ "archy": {
+ "version": "0.0.2"
+ },
+ "bower-config": {
+ "version": "0.5.0",
+ "dependencies": {
+ "mout": {
+ "version": "0.6.0"
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "dependencies": {
+ "wordwrap": {
+ "version": "0.0.2"
+ },
+ "minimist": {
+ "version": "0.0.8"
+ }
+ }
+ }
+ }
+ },
+ "bower-endpoint-parser": {
+ "version": "0.2.1"
+ },
+ "bower-json": {
+ "version": "0.4.0",
+ "dependencies": {
+ "deep-extend": {
+ "version": "0.2.8"
+ },
+ "intersect": {
+ "version": "0.0.3"
+ }
+ }
+ },
+ "bower-logger": {
+ "version": "0.2.2"
+ },
+ "bower-registry-client": {
+ "version": "0.1.6",
+ "dependencies": {
+ "async": {
+ "version": "0.2.10"
+ },
+ "bower-config": {
+ "version": "0.4.5",
+ "dependencies": {
+ "mout": {
+ "version": "0.6.0"
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "dependencies": {
+ "wordwrap": {
+ "version": "0.0.2"
+ },
+ "minimist": {
+ "version": "0.0.8"
+ }
+ }
+ }
+ }
+ },
+ "request-replay": {
+ "version": "0.2.0"
+ }
+ }
+ },
+ "cardinal": {
+ "version": "0.4.4",
+ "dependencies": {
+ "redeyed": {
+ "version": "0.4.4",
+ "dependencies": {
+ "esprima": {
+ "version": "1.0.4"
+ }
+ }
+ },
+ "ansicolors": {
+ "version": "0.2.1"
+ }
+ }
+ },
+ "chalk": {
+ "version": "0.2.1",
+ "dependencies": {
+ "has-color": {
+ "version": "0.1.4"
+ },
+ "ansi-styles": {
+ "version": "0.2.0"
+ }
+ }
+ },
+ "chmodr": {
+ "version": "0.1.0"
+ },
+ "decompress-zip": {
+ "version": "0.0.4",
+ "dependencies": {
+ "mkpath": {
+ "version": "0.1.0"
+ },
+ "binary": {
+ "version": "0.3.0",
+ "dependencies": {
+ "chainsaw": {
+ "version": "0.1.0",
+ "dependencies": {
+ "traverse": {
+ "version": "0.3.9"
+ }
+ }
+ },
+ "buffers": {
+ "version": "0.1.1"
+ }
+ }
+ },
+ "touch": {
+ "version": "0.0.2",
+ "dependencies": {
+ "nopt": {
+ "version": "1.0.10"
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "1.1.11",
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.1"
+ },
+ "string_decoder": {
+ "version": "0.10.25-1"
+ },
+ "debuglog": {
+ "version": "0.0.2"
+ }
+ }
+ }
+ }
+ },
+ "fstream": {
+ "version": "0.1.25",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ },
+ "fstream-ignore": {
+ "version": "0.0.7",
+ "dependencies": {
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ },
+ "glob": {
+ "version": "3.2.9",
+ "dependencies": {
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ },
+ "graceful-fs": {
+ "version": "2.0.2"
+ },
+ "handlebars": {
+ "version": "1.0.12",
+ "dependencies": {
+ "optimist": {
+ "version": "0.3.7",
+ "dependencies": {
+ "wordwrap": {
+ "version": "0.0.2"
+ }
+ }
+ },
+ "uglify-js": {
+ "version": "2.3.6",
+ "dependencies": {
+ "async": {
+ "version": "0.2.10"
+ },
+ "source-map": {
+ "version": "0.1.32",
+ "dependencies": {
+ "amdefine": {
+ "version": "0.1.0"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "inquirer": {
+ "version": "0.3.5",
+ "dependencies": {
+ "lodash": {
+ "version": "1.2.1"
+ },
+ "async": {
+ "version": "0.2.10"
+ },
+ "cli-color": {
+ "version": "0.2.3",
+ "dependencies": {
+ "es5-ext": {
+ "version": "0.9.2"
+ },
+ "memoizee": {
+ "version": "0.2.6",
+ "dependencies": {
+ "event-emitter": {
+ "version": "0.2.2"
+ },
+ "next-tick": {
+ "version": "0.1.0"
+ }
+ }
+ }
+ }
+ },
+ "mute-stream": {
+ "version": "0.0.3"
+ }
+ }
+ },
+ "junk": {
+ "version": "0.2.2"
+ },
+ "mkdirp": {
+ "version": "0.3.5"
+ },
+ "mout": {
+ "version": "0.7.1"
+ },
+ "nopt": {
+ "version": "2.1.2"
+ },
+ "lru-cache": {
+ "version": "2.3.1"
+ },
+ "open": {
+ "version": "0.0.4"
+ },
+ "osenv": {
+ "version": "0.0.3"
+ },
+ "promptly": {
+ "version": "0.2.0",
+ "dependencies": {
+ "read": {
+ "version": "1.0.5",
+ "dependencies": {
+ "mute-stream": {
+ "version": "0.0.4"
+ }
+ }
+ }
+ }
+ },
+ "q": {
+ "version": "0.9.7"
+ },
+ "request": {
+ "version": "2.27.0",
+ "dependencies": {
+ "qs": {
+ "version": "0.6.6"
+ },
+ "json-stringify-safe": {
+ "version": "5.0.0"
+ },
+ "forever-agent": {
+ "version": "0.5.2"
+ },
+ "tunnel-agent": {
+ "version": "0.3.0"
+ },
+ "http-signature": {
+ "version": "0.10.0",
+ "dependencies": {
+ "assert-plus": {
+ "version": "0.1.2"
+ },
+ "asn1": {
+ "version": "0.1.11"
+ },
+ "ctype": {
+ "version": "0.5.2"
+ }
+ }
+ },
+ "hawk": {
+ "version": "1.0.0",
+ "dependencies": {
+ "hoek": {
+ "version": "0.9.1"
+ },
+ "boom": {
+ "version": "0.4.2"
+ },
+ "cryptiles": {
+ "version": "0.2.2"
+ },
+ "sntp": {
+ "version": "0.2.4"
+ }
+ }
+ },
+ "aws-sign": {
+ "version": "0.3.0"
+ },
+ "oauth-sign": {
+ "version": "0.3.0"
+ },
+ "cookie-jar": {
+ "version": "0.3.0"
+ },
+ "node-uuid": {
+ "version": "1.4.1"
+ },
+ "mime": {
+ "version": "1.2.11"
+ },
+ "form-data": {
+ "version": "0.1.2",
+ "dependencies": {
+ "combined-stream": {
+ "version": "0.0.4",
+ "dependencies": {
+ "delayed-stream": {
+ "version": "0.0.5"
+ }
+ }
+ },
+ "async": {
+ "version": "0.2.10"
+ }
+ }
+ }
+ }
+ },
+ "request-progress": {
+ "version": "0.3.1",
+ "dependencies": {
+ "throttleit": {
+ "version": "0.0.2"
+ }
+ }
+ },
+ "retry": {
+ "version": "0.6.0"
+ },
+ "rimraf": {
+ "version": "2.2.6"
+ },
+ "stringify-object": {
+ "version": "0.1.8"
+ },
+ "sudo-block": {
+ "version": "0.2.1",
+ "dependencies": {
+ "chalk": {
+ "version": "0.1.1",
+ "dependencies": {
+ "has-color": {
+ "version": "0.1.4"
+ },
+ "ansi-styles": {
+ "version": "0.1.2"
+ }
+ }
+ }
+ }
+ },
+ "tar": {
+ "version": "0.1.19",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1"
+ },
+ "block-stream": {
+ "version": "0.0.7"
+ }
+ }
+ },
+ "tmp": {
+ "version": "0.0.23"
+ },
+ "update-notifier": {
+ "version": "0.1.7",
+ "dependencies": {
+ "configstore": {
+ "version": "0.1.7",
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.1"
+ },
+ "js-yaml": {
+ "version": "2.1.3",
+ "dependencies": {
+ "argparse": {
+ "version": "0.1.15",
+ "dependencies": {
+ "underscore": {
+ "version": "1.4.4"
+ },
+ "underscore.string": {
+ "version": "2.3.3"
+ }
+ }
+ },
+ "esprima": {
+ "version": "1.0.4"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "which": {
+ "version": "1.0.5"
+ },
+ "p-throttler": {
+ "version": "0.0.1"
+ }
+ }
+ },
+ "browserstacktunnel-wrapper": {
+ "version": "1.1.2"
+ },
+ "canonical-path": {
+ "version": "0.0.2"
+ },
+ "dgeni": {
+ "version": "0.2.2",
+ "dependencies": {
+ "rimraf": {
+ "version": "2.2.6"
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "dependencies": {
+ "wordwrap": {
+ "version": "0.0.2"
+ },
+ "minimist": {
+ "version": "0.0.8"
+ }
+ }
+ },
+ "lodash": {
+ "version": "2.4.1"
+ },
+ "dependency-graph": {
+ "version": "0.1.0",
+ "dependencies": {
+ "underscore": {
+ "version": "1.4.4"
+ }
+ }
+ },
+ "q": {
+ "version": "0.9.7"
+ },
+ "di": {
+ "version": "0.0.1"
+ },
+ "marked": {
+ "version": "0.2.10"
+ },
+ "glob": {
+ "version": "3.2.9",
+ "dependencies": {
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ }
+ }
+ },
+ "dgeni-packages": {
+ "version": "0.8.1",
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.1"
+ },
+ "graceful-fs": {
+ "version": "2.0.2"
+ },
+ "glob": {
+ "version": "3.2.9",
+ "dependencies": {
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ },
+ "nunjucks": {
+ "version": "1.0.1",
+ "dependencies": {
+ "optimist": {
+ "version": "0.6.1",
+ "dependencies": {
+ "wordwrap": {
+ "version": "0.0.2"
+ },
+ "minimist": {
+ "version": "0.0.8"
+ }
+ }
+ }
+ }
+ },
+ "catharsis": {
+ "version": "0.7.0"
+ },
+ "esprima": {
+ "version": "1.0.4"
+ }
+ }
+ },
+ "event-stream": {
+ "version": "3.1.0",
+ "dependencies": {
+ "through": {
+ "version": "2.3.4"
+ },
+ "duplexer": {
+ "version": "0.1.1"
+ },
+ "from": {
+ "version": "0.1.3"
+ },
+ "map-stream": {
+ "version": "0.1.0"
+ },
+ "pause-stream": {
+ "version": "0.0.11"
+ },
+ "split": {
+ "version": "0.2.10"
+ },
+ "stream-combiner": {
+ "version": "0.0.4"
+ }
+ }
+ },
+ "grunt": {
+ "version": "0.4.2",
+ "dependencies": {
+ "async": {
+ "version": "0.1.22"
+ },
+ "coffee-script": {
+ "version": "1.3.3"
+ },
+ "colors": {
+ "version": "0.6.2"
+ },
+ "dateformat": {
+ "version": "1.0.2-1.2.3"
+ },
+ "eventemitter2": {
+ "version": "0.4.13"
+ },
+ "findup-sync": {
+ "version": "0.1.2",
+ "dependencies": {
+ "lodash": {
+ "version": "1.0.1"
+ }
+ }
+ },
+ "glob": {
+ "version": "3.1.21",
+ "dependencies": {
+ "graceful-fs": {
+ "version": "1.2.3"
+ },
+ "inherits": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "hooker": {
+ "version": "0.2.3"
+ },
+ "iconv-lite": {
+ "version": "0.2.11"
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "nopt": {
+ "version": "1.0.10",
+ "dependencies": {
+ "abbrev": {
+ "version": "1.0.4"
+ }
+ }
+ },
+ "rimraf": {
+ "version": "2.0.3",
+ "dependencies": {
+ "graceful-fs": {
+ "version": "1.1.14"
+ }
+ }
+ },
+ "lodash": {
+ "version": "0.9.2"
+ },
+ "underscore.string": {
+ "version": "2.2.1"
+ },
+ "which": {
+ "version": "1.0.5"
+ },
+ "js-yaml": {
+ "version": "2.0.5",
+ "dependencies": {
+ "argparse": {
+ "version": "0.1.15",
+ "dependencies": {
+ "underscore": {
+ "version": "1.4.4"
+ },
+ "underscore.string": {
+ "version": "2.3.3"
+ }
+ }
+ },
+ "esprima": {
+ "version": "1.0.4"
+ }
+ }
+ },
+ "exit": {
+ "version": "0.1.2"
+ },
+ "getobject": {
+ "version": "0.1.0"
+ }
+ }
+ },
+ "grunt-bump": {
+ "version": "0.0.13",
+ "dependencies": {
+ "semver": {
+ "version": "1.1.4"
+ }
+ }
+ },
+ "grunt-contrib-clean": {
+ "version": "0.5.0",
+ "dependencies": {
+ "rimraf": {
+ "version": "2.2.6"
+ }
+ }
+ },
+ "grunt-contrib-compress": {
+ "version": "0.5.3",
+ "dependencies": {
+ "archiver": {
+ "version": "0.4.10",
+ "dependencies": {
+ "readable-stream": {
+ "version": "1.0.26",
+ "dependencies": {
+ "string_decoder": {
+ "version": "0.10.25-1"
+ }
+ }
+ },
+ "iconv-lite": {
+ "version": "0.2.11"
+ }
+ }
+ },
+ "lazystream": {
+ "version": "0.1.0",
+ "dependencies": {
+ "readable-stream": {
+ "version": "1.0.26",
+ "dependencies": {
+ "string_decoder": {
+ "version": "0.10.25-1"
+ }
+ }
+ }
+ }
+ },
+ "prettysize": {
+ "version": "0.0.3"
+ }
+ }
+ },
+ "grunt-contrib-connect": {
+ "version": "0.5.0",
+ "dependencies": {
+ "connect": {
+ "version": "2.7.11",
+ "dependencies": {
+ "qs": {
+ "version": "0.6.5"
+ },
+ "formidable": {
+ "version": "1.0.14"
+ },
+ "cookie-signature": {
+ "version": "1.0.1"
+ },
+ "buffer-crc32": {
+ "version": "0.2.1"
+ },
+ "cookie": {
+ "version": "0.0.5"
+ },
+ "send": {
+ "version": "0.1.1",
+ "dependencies": {
+ "mime": {
+ "version": "1.2.11"
+ },
+ "range-parser": {
+ "version": "0.0.4"
+ }
+ }
+ },
+ "bytes": {
+ "version": "0.2.0"
+ },
+ "fresh": {
+ "version": "0.1.0"
+ },
+ "pause": {
+ "version": "0.0.1"
+ },
+ "debug": {
+ "version": "0.7.4"
+ }
+ }
+ },
+ "connect-livereload": {
+ "version": "0.2.0"
+ },
+ "open": {
+ "version": "0.0.4"
+ }
+ }
+ },
+ "grunt-contrib-copy": {
+ "version": "0.4.1"
+ },
+ "grunt-contrib-jshint": {
+ "version": "0.7.2",
+ "dependencies": {
+ "jshint": {
+ "version": "2.3.0",
+ "dependencies": {
+ "shelljs": {
+ "version": "0.1.4"
+ },
+ "underscore": {
+ "version": "1.4.4"
+ },
+ "cli": {
+ "version": "0.4.5",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.9",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ }
+ }
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "console-browserify": {
+ "version": "0.1.6"
+ }
+ }
+ }
+ }
+ },
+ "grunt-ddescribe-iit": {
+ "version": "0.0.4"
+ },
+ "grunt-jasmine-node": {
+ "version": "0.1.0",
+ "from": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
+ "resolved": "git://github.com/vojtajina/grunt-jasmine-node.git#ced17cbe52c1412b2ada53160432a5b681f37cd7"
+ },
+ "grunt-jscs-checker": {
+ "version": "0.4.0",
+ "dependencies": {
+ "hooker": {
+ "version": "0.2.3"
+ },
+ "jscs": {
+ "version": "1.3.0",
+ "dependencies": {
+ "esprima": {
+ "version": "1.0.3"
+ },
+ "vow": {
+ "version": "0.3.9"
+ },
+ "vow-fs": {
+ "version": "0.2.3",
+ "dependencies": {
+ "node-uuid": {
+ "version": "1.4.0"
+ },
+ "vow-queue": {
+ "version": "0.0.2"
+ }
+ }
+ },
+ "colors": {
+ "version": "0.6.0-1"
+ },
+ "commander": {
+ "version": "1.2.0",
+ "dependencies": {
+ "keypress": {
+ "version": "0.1.0"
+ }
+ }
+ },
+ "minimatch": {
+ "version": "0.2.12",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "glob": {
+ "version": "3.2.7",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ },
+ "xmlbuilder": {
+ "version": "1.1.2",
+ "dependencies": {
+ "underscore": {
+ "version": "1.6.0"
+ }
+ }
+ },
+ "strip-json-comments": {
+ "version": "0.1.1"
+ }
+ }
+ },
+ "lodash.assign": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._basecreatecallback": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash.bind": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._createwrapper": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._basebind": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._basecreate": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ },
+ "lodash.noop": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.isobject": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._basecreatewrapper": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._basecreate": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ },
+ "lodash.noop": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.isobject": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.isfunction": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._slice": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.identity": {
+ "version": "2.4.1"
+ },
+ "lodash._setbinddata": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ },
+ "lodash.noop": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.support": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ }
+ }
+ }
+ }
+ },
+ "lodash.keys": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ },
+ "lodash.isobject": {
+ "version": "2.4.1"
+ },
+ "lodash._shimkeys": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "vow": {
+ "version": "0.4.1"
+ }
+ }
+ },
+ "grunt-merge-conflict": {
+ "version": "0.0.2"
+ },
+ "grunt-parallel": {
+ "version": "0.3.1",
+ "dependencies": {
+ "q": {
+ "version": "0.8.12"
+ },
+ "lpad": {
+ "version": "0.1.0"
+ }
+ }
+ },
+ "grunt-shell": {
+ "version": "0.4.0",
+ "dependencies": {
+ "stripcolorcodes": {
+ "version": "0.1.0"
+ }
+ }
+ },
+ "gulp": {
+ "version": "3.4.0",
+ "dependencies": {
+ "optimist": {
+ "version": "0.6.1",
+ "dependencies": {
+ "wordwrap": {
+ "version": "0.0.2"
+ },
+ "minimist": {
+ "version": "0.0.8"
+ }
+ }
+ },
+ "gulp-util": {
+ "version": "2.2.14",
+ "dependencies": {
+ "chalk": {
+ "version": "0.4.0",
+ "dependencies": {
+ "has-color": {
+ "version": "0.1.4"
+ },
+ "ansi-styles": {
+ "version": "1.0.0"
+ },
+ "strip-ansi": {
+ "version": "0.1.1"
+ }
+ }
+ },
+ "lodash.template": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash.defaults": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.escape": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._escapehtmlchar": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._htmlescapes": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._reunescapedhtml": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._htmlescapes": {
+ "version": "2.4.1"
+ }
+ }
+ }
+ }
+ },
+ "lodash._escapestringchar": {
+ "version": "2.4.1"
+ },
+ "lodash.keys": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ },
+ "lodash.isobject": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._shimkeys": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ }
+ }
+ },
+ "lodash.templatesettings": {
+ "version": "2.4.1"
+ },
+ "lodash.values": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._reinterpolate": {
+ "version": "2.4.1"
+ },
+ "vinyl": {
+ "version": "0.2.3",
+ "dependencies": {
+ "clone-stats": {
+ "version": "0.0.1"
+ }
+ }
+ },
+ "through2": {
+ "version": "0.4.1",
+ "dependencies": {
+ "readable-stream": {
+ "version": "1.0.26",
+ "dependencies": {
+ "string_decoder": {
+ "version": "0.10.25-1"
+ }
+ }
+ },
+ "xtend": {
+ "version": "2.1.2",
+ "dependencies": {
+ "object-keys": {
+ "version": "0.4.0"
+ }
+ }
+ }
+ }
+ },
+ "dateformat": {
+ "version": "1.0.7-1.2.3"
+ },
+ "multipipe": {
+ "version": "0.0.2",
+ "dependencies": {
+ "duplexer2": {
+ "version": "0.0.1"
+ }
+ }
+ },
+ "minimist": {
+ "version": "0.0.8"
+ }
+ }
+ },
+ "orchestrator": {
+ "version": "0.3.3",
+ "dependencies": {
+ "sequencify": {
+ "version": "0.0.7"
+ }
+ }
+ },
+ "resolve": {
+ "version": "0.6.1"
+ },
+ "findup-sync": {
+ "version": "0.1.2",
+ "dependencies": {
+ "glob": {
+ "version": "3.1.21",
+ "dependencies": {
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "graceful-fs": {
+ "version": "1.2.3"
+ },
+ "inherits": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "lodash": {
+ "version": "1.0.1"
+ }
+ }
+ },
+ "pretty-hrtime": {
+ "version": "0.2.0"
+ },
+ "vinyl-fs": {
+ "version": "0.0.1",
+ "dependencies": {
+ "vinyl": {
+ "version": "0.2.3",
+ "dependencies": {
+ "clone-stats": {
+ "version": "0.0.1"
+ }
+ }
+ },
+ "glob-stream": {
+ "version": "3.1.9",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.9",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "ordered-read-streams": {
+ "version": "0.0.7"
+ },
+ "glob2base": {
+ "version": "0.0.8"
+ },
+ "unique-stream": {
+ "version": "0.0.3"
+ },
+ "through": {
+ "version": "2.3.4"
+ }
+ }
+ },
+ "glob-watcher": {
+ "version": "0.0.3",
+ "dependencies": {
+ "gaze": {
+ "version": "0.4.3",
+ "dependencies": {
+ "globule": {
+ "version": "0.1.0",
+ "dependencies": {
+ "lodash": {
+ "version": "1.0.1"
+ },
+ "glob": {
+ "version": "3.1.21",
+ "dependencies": {
+ "graceful-fs": {
+ "version": "1.2.3"
+ },
+ "inherits": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "0.3.5"
+ },
+ "graceful-fs": {
+ "version": "2.0.2"
+ },
+ "map-stream": {
+ "version": "0.1.0"
+ }
+ }
+ },
+ "semver": {
+ "version": "2.2.1"
+ },
+ "archy": {
+ "version": "0.0.2"
+ }
+ }
+ },
+ "gulp-concat": {
+ "version": "2.1.7",
+ "dependencies": {
+ "through": {
+ "version": "2.3.4"
+ },
+ "gulp-util": {
+ "version": "2.2.14",
+ "dependencies": {
+ "chalk": {
+ "version": "0.4.0",
+ "dependencies": {
+ "has-color": {
+ "version": "0.1.4"
+ },
+ "ansi-styles": {
+ "version": "1.0.0"
+ },
+ "strip-ansi": {
+ "version": "0.1.1"
+ }
+ }
+ },
+ "lodash.template": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash.defaults": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.escape": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._escapehtmlchar": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._htmlescapes": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._reunescapedhtml": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._htmlescapes": {
+ "version": "2.4.1"
+ }
+ }
+ }
+ }
+ },
+ "lodash._escapestringchar": {
+ "version": "2.4.1"
+ },
+ "lodash.keys": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ },
+ "lodash.isobject": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._shimkeys": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ }
+ }
+ },
+ "lodash.templatesettings": {
+ "version": "2.4.1"
+ },
+ "lodash.values": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._reinterpolate": {
+ "version": "2.4.1"
+ },
+ "vinyl": {
+ "version": "0.2.3",
+ "dependencies": {
+ "clone-stats": {
+ "version": "0.0.1"
+ }
+ }
+ },
+ "through2": {
+ "version": "0.4.1",
+ "dependencies": {
+ "readable-stream": {
+ "version": "1.0.26",
+ "dependencies": {
+ "string_decoder": {
+ "version": "0.10.25-1"
+ }
+ }
+ },
+ "xtend": {
+ "version": "2.1.2",
+ "dependencies": {
+ "object-keys": {
+ "version": "0.4.0"
+ }
+ }
+ }
+ }
+ },
+ "dateformat": {
+ "version": "1.0.7-1.2.3"
+ },
+ "multipipe": {
+ "version": "0.0.2",
+ "dependencies": {
+ "duplexer2": {
+ "version": "0.0.1"
+ }
+ }
+ },
+ "minimist": {
+ "version": "0.0.8"
+ }
+ }
+ }
+ }
+ },
+ "gulp-jshint": {
+ "version": "1.4.2",
+ "dependencies": {
+ "map-stream": {
+ "version": "0.1.0"
+ },
+ "jshint": {
+ "version": "2.4.4",
+ "dependencies": {
+ "shelljs": {
+ "version": "0.1.4"
+ },
+ "underscore": {
+ "version": "1.4.4"
+ },
+ "cli": {
+ "version": "0.4.5",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.9",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ }
+ }
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "htmlparser2": {
+ "version": "3.3.0",
+ "dependencies": {
+ "domhandler": {
+ "version": "2.1.0"
+ },
+ "domutils": {
+ "version": "1.1.6"
+ },
+ "domelementtype": {
+ "version": "1.1.1"
+ },
+ "readable-stream": {
+ "version": "1.0.26",
+ "dependencies": {
+ "string_decoder": {
+ "version": "0.10.25-1"
+ }
+ }
+ }
+ }
+ },
+ "console-browserify": {
+ "version": "0.1.6"
+ },
+ "exit": {
+ "version": "0.1.2"
+ }
+ }
+ },
+ "gulp-util": {
+ "version": "2.2.14",
+ "dependencies": {
+ "chalk": {
+ "version": "0.4.0",
+ "dependencies": {
+ "has-color": {
+ "version": "0.1.4"
+ },
+ "ansi-styles": {
+ "version": "1.0.0"
+ },
+ "strip-ansi": {
+ "version": "0.1.1"
+ }
+ }
+ },
+ "lodash.template": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash.defaults": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.escape": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._escapehtmlchar": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._htmlescapes": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._reunescapedhtml": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._htmlescapes": {
+ "version": "2.4.1"
+ }
+ }
+ }
+ }
+ },
+ "lodash._escapestringchar": {
+ "version": "2.4.1"
+ },
+ "lodash.keys": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ },
+ "lodash.isobject": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._shimkeys": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ }
+ }
+ },
+ "lodash.templatesettings": {
+ "version": "2.4.1"
+ },
+ "lodash.values": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._reinterpolate": {
+ "version": "2.4.1"
+ },
+ "vinyl": {
+ "version": "0.2.3",
+ "dependencies": {
+ "clone-stats": {
+ "version": "0.0.1"
+ }
+ }
+ },
+ "through2": {
+ "version": "0.4.1",
+ "dependencies": {
+ "readable-stream": {
+ "version": "1.0.26",
+ "dependencies": {
+ "string_decoder": {
+ "version": "0.10.25-1"
+ }
+ }
+ },
+ "xtend": {
+ "version": "2.1.2",
+ "dependencies": {
+ "object-keys": {
+ "version": "0.4.0"
+ }
+ }
+ }
+ }
+ },
+ "dateformat": {
+ "version": "1.0.7-1.2.3"
+ },
+ "multipipe": {
+ "version": "0.0.2",
+ "dependencies": {
+ "duplexer2": {
+ "version": "0.0.1"
+ }
+ }
+ },
+ "minimist": {
+ "version": "0.0.8"
+ }
+ }
+ },
+ "lodash.clone": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._baseclone": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash.assign": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash.keys": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ },
+ "lodash._shimkeys": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.foreach": {
+ "version": "2.4.1"
+ },
+ "lodash.forown": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash.keys": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ },
+ "lodash._shimkeys": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._getarray": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._arraypool": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.isarray": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.isobject": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._releasearray": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._arraypool": {
+ "version": "2.4.1"
+ },
+ "lodash._maxpoolsize": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._slice": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._basecreatecallback": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash.bind": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._createwrapper": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._basebind": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._basecreate": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ },
+ "lodash.noop": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.isobject": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ }
+ }
+ },
+ "lodash._basecreatewrapper": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._basecreate": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ },
+ "lodash.noop": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.isobject": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ }
+ }
+ },
+ "lodash.isfunction": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._slice": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.identity": {
+ "version": "2.4.1"
+ },
+ "lodash._setbinddata": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ },
+ "lodash.noop": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash.support": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "jasmine-node": {
+ "version": "1.11.0",
+ "dependencies": {
+ "coffee-script": {
+ "version": "1.7.1"
+ },
+ "jasmine-growl-reporter": {
+ "version": "0.0.2",
+ "dependencies": {
+ "growl": {
+ "version": "1.7.0"
+ }
+ }
+ },
+ "requirejs": {
+ "version": "2.1.11"
+ },
+ "walkdir": {
+ "version": "0.0.7"
+ },
+ "underscore": {
+ "version": "1.6.0"
+ },
+ "gaze": {
+ "version": "0.3.4",
+ "dependencies": {
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "fileset": {
+ "version": "0.1.5",
+ "dependencies": {
+ "glob": {
+ "version": "3.2.9",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "0.3.5"
+ }
+ }
+ },
+ "jasmine-reporters": {
+ "version": "0.2.1"
+ },
+ "jshint-stylish": {
+ "version": "0.1.5",
+ "dependencies": {
+ "chalk": {
+ "version": "0.4.0",
+ "dependencies": {
+ "has-color": {
+ "version": "0.1.4"
+ },
+ "ansi-styles": {
+ "version": "1.0.0"
+ },
+ "strip-ansi": {
+ "version": "0.1.1"
+ }
+ }
+ },
+ "text-table": {
+ "version": "0.2.0"
+ }
+ }
+ },
+ "karma": {
+ "version": "0.12.0",
+ "dependencies": {
+ "di": {
+ "version": "0.0.1"
+ },
+ "socket.io": {
+ "version": "0.9.16",
+ "dependencies": {
+ "socket.io-client": {
+ "version": "0.9.16",
+ "dependencies": {
+ "uglify-js": {
+ "version": "1.2.5"
+ },
+ "ws": {
+ "version": "0.4.31",
+ "dependencies": {
+ "commander": {
+ "version": "0.6.1"
+ },
+ "nan": {
+ "version": "0.3.2"
+ },
+ "tinycolor": {
+ "version": "0.0.1"
+ },
+ "options": {
+ "version": "0.0.5"
+ }
+ }
+ },
+ "xmlhttprequest": {
+ "version": "1.4.2"
+ },
+ "active-x-obfuscator": {
+ "version": "0.0.1",
+ "dependencies": {
+ "zeparser": {
+ "version": "0.0.5"
+ }
+ }
+ }
+ }
+ },
+ "policyfile": {
+ "version": "0.0.4"
+ },
+ "base64id": {
+ "version": "0.1.0"
+ },
+ "redis": {
+ "version": "0.7.3"
+ }
+ }
+ },
+ "chokidar": {
+ "version": "0.8.1"
+ },
+ "glob": {
+ "version": "3.2.9",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "http-proxy": {
+ "version": "0.10.4",
+ "dependencies": {
+ "pkginfo": {
+ "version": "0.3.0"
+ },
+ "utile": {
+ "version": "0.2.1",
+ "dependencies": {
+ "async": {
+ "version": "0.2.10"
+ },
+ "deep-equal": {
+ "version": "0.2.1"
+ },
+ "i": {
+ "version": "0.3.2"
+ },
+ "mkdirp": {
+ "version": "0.3.5"
+ },
+ "ncp": {
+ "version": "0.4.2"
+ }
+ }
+ }
+ }
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "dependencies": {
+ "wordwrap": {
+ "version": "0.0.2"
+ },
+ "minimist": {
+ "version": "0.0.8"
+ }
+ }
+ },
+ "rimraf": {
+ "version": "2.2.6"
+ },
+ "q": {
+ "version": "0.9.7"
+ },
+ "colors": {
+ "version": "0.6.2"
+ },
+ "lodash": {
+ "version": "2.4.1"
+ },
+ "mime": {
+ "version": "1.2.11"
+ },
+ "log4js": {
+ "version": "0.6.12",
+ "dependencies": {
+ "async": {
+ "version": "0.1.15"
+ },
+ "semver": {
+ "version": "1.1.4"
+ },
+ "readable-stream": {
+ "version": "1.0.26-2",
+ "dependencies": {
+ "string_decoder": {
+ "version": "0.10.25-1"
+ }
+ }
+ }
+ }
+ },
+ "useragent": {
+ "version": "2.0.7",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.2.4"
+ }
+ }
+ },
+ "graceful-fs": {
+ "version": "2.0.2"
+ },
+ "connect": {
+ "version": "2.12.0",
+ "dependencies": {
+ "batch": {
+ "version": "0.5.0"
+ },
+ "qs": {
+ "version": "0.6.6"
+ },
+ "cookie-signature": {
+ "version": "1.0.1"
+ },
+ "buffer-crc32": {
+ "version": "0.2.1"
+ },
+ "cookie": {
+ "version": "0.1.0"
+ },
+ "send": {
+ "version": "0.1.4",
+ "dependencies": {
+ "range-parser": {
+ "version": "0.0.4"
+ }
+ }
+ },
+ "bytes": {
+ "version": "0.2.1"
+ },
+ "fresh": {
+ "version": "0.2.0"
+ },
+ "pause": {
+ "version": "0.0.1"
+ },
+ "uid2": {
+ "version": "0.0.3"
+ },
+ "debug": {
+ "version": "0.7.4"
+ },
+ "methods": {
+ "version": "0.1.0"
+ },
+ "raw-body": {
+ "version": "1.1.2"
+ },
+ "negotiator": {
+ "version": "0.3.0"
+ },
+ "multiparty": {
+ "version": "2.2.0",
+ "dependencies": {
+ "readable-stream": {
+ "version": "1.1.11",
+ "dependencies": {
+ "core-util-is": {
+ "version": "1.0.1"
+ },
+ "string_decoder": {
+ "version": "0.10.25-1"
+ },
+ "debuglog": {
+ "version": "0.0.2"
+ }
+ }
+ },
+ "stream-counter": {
+ "version": "0.2.0"
+ }
+ }
+ }
+ }
+ },
+ "source-map": {
+ "version": "0.1.33",
+ "dependencies": {
+ "amdefine": {
+ "version": "0.1.0"
+ }
+ }
+ }
+ }
+ },
+ "karma-browserstack-launcher": {
+ "version": "0.0.7",
+ "dependencies": {
+ "browserstack": {
+ "version": "1.0.1"
+ },
+ "q": {
+ "version": "0.9.7"
+ }
+ }
+ },
+ "karma-chrome-launcher": {
+ "version": "0.1.2"
+ },
+ "karma-firefox-launcher": {
+ "version": "0.1.3"
+ },
+ "karma-jasmine": {
+ "version": "0.1.5"
+ },
+ "karma-junit-reporter": {
+ "version": "0.2.1",
+ "dependencies": {
+ "xmlbuilder": {
+ "version": "0.4.2"
+ }
+ }
+ },
+ "karma-ng-scenario": {
+ "version": "0.1.0"
+ },
+ "karma-sauce-launcher": {
+ "version": "0.2.0",
+ "dependencies": {
+ "wd": {
+ "version": "0.2.10",
+ "dependencies": {
+ "async": {
+ "version": "0.2.10"
+ },
+ "vargs": {
+ "version": "0.1.0"
+ },
+ "q": {
+ "version": "1.0.0"
+ },
+ "request": {
+ "version": "2.33.0",
+ "dependencies": {
+ "qs": {
+ "version": "0.6.6"
+ },
+ "json-stringify-safe": {
+ "version": "5.0.0"
+ },
+ "forever-agent": {
+ "version": "0.5.2"
+ },
+ "node-uuid": {
+ "version": "1.4.1"
+ },
+ "mime": {
+ "version": "1.2.11"
+ },
+ "tough-cookie": {
+ "version": "0.12.1",
+ "dependencies": {
+ "punycode": {
+ "version": "1.2.4"
+ }
+ }
+ },
+ "form-data": {
+ "version": "0.1.2",
+ "dependencies": {
+ "combined-stream": {
+ "version": "0.0.4",
+ "dependencies": {
+ "delayed-stream": {
+ "version": "0.0.5"
+ }
+ }
+ }
+ }
+ },
+ "tunnel-agent": {
+ "version": "0.3.0"
+ },
+ "http-signature": {
+ "version": "0.10.0",
+ "dependencies": {
+ "assert-plus": {
+ "version": "0.1.2"
+ },
+ "asn1": {
+ "version": "0.1.11"
+ },
+ "ctype": {
+ "version": "0.5.2"
+ }
+ }
+ },
+ "oauth-sign": {
+ "version": "0.3.0"
+ },
+ "hawk": {
+ "version": "1.0.0",
+ "dependencies": {
+ "hoek": {
+ "version": "0.9.1"
+ },
+ "boom": {
+ "version": "0.4.2"
+ },
+ "cryptiles": {
+ "version": "0.2.2"
+ },
+ "sntp": {
+ "version": "0.2.4"
+ }
+ }
+ },
+ "aws-sign2": {
+ "version": "0.5.0"
+ }
+ }
+ },
+ "archiver": {
+ "version": "0.5.2",
+ "dependencies": {
+ "readable-stream": {
+ "version": "1.0.26",
+ "dependencies": {
+ "string_decoder": {
+ "version": "0.10.25-1"
+ }
+ }
+ },
+ "zip-stream": {
+ "version": "0.1.4",
+ "dependencies": {
+ "lodash.defaults": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash.keys": {
+ "version": "2.4.1",
+ "dependencies": {
+ "lodash._isnative": {
+ "version": "2.4.1"
+ },
+ "lodash.isobject": {
+ "version": "2.4.1"
+ },
+ "lodash._shimkeys": {
+ "version": "2.4.1"
+ }
+ }
+ },
+ "lodash._objecttypes": {
+ "version": "2.4.1"
+ }
+ }
+ }
+ }
+ },
+ "lazystream": {
+ "version": "0.1.0"
+ },
+ "file-utils": {
+ "version": "0.1.5",
+ "dependencies": {
+ "lodash": {
+ "version": "2.1.0"
+ },
+ "iconv-lite": {
+ "version": "0.2.11"
+ },
+ "rimraf": {
+ "version": "2.2.6"
+ },
+ "glob": {
+ "version": "3.2.9",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "findup-sync": {
+ "version": "0.1.2",
+ "dependencies": {
+ "glob": {
+ "version": "3.1.21",
+ "dependencies": {
+ "graceful-fs": {
+ "version": "1.2.3"
+ },
+ "inherits": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "lodash": {
+ "version": "1.0.1"
+ }
+ }
+ },
+ "isbinaryfile": {
+ "version": "0.1.9"
+ }
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "2.4.1"
+ },
+ "underscore.string": {
+ "version": "2.3.3"
+ }
+ }
+ },
+ "sauce-connect-launcher": {
+ "version": "0.2.2",
+ "dependencies": {
+ "lodash": {
+ "version": "1.3.1"
+ },
+ "async": {
+ "version": "0.2.10"
+ },
+ "adm-zip": {
+ "version": "0.4.4"
+ }
+ }
+ },
+ "q": {
+ "version": "0.9.7"
+ },
+ "saucelabs": {
+ "version": "0.1.1"
+ }
+ }
+ },
+ "karma-script-launcher": {
+ "version": "0.1.0"
+ },
+ "load-grunt-tasks": {
+ "version": "0.3.0",
+ "dependencies": {
+ "globule": {
+ "version": "0.2.0",
+ "dependencies": {
+ "lodash": {
+ "version": "2.4.1"
+ },
+ "glob": {
+ "version": "3.2.9",
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ },
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ }
+ }
+ },
+ "findup-sync": {
+ "version": "0.1.2",
+ "dependencies": {
+ "glob": {
+ "version": "3.1.21",
+ "dependencies": {
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "graceful-fs": {
+ "version": "1.2.3"
+ },
+ "inherits": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "lodash": {
+ "version": "1.0.1"
+ }
+ }
+ }
+ }
+ },
+ "lodash": {
+ "version": "2.1.0"
+ },
+ "marked": {
+ "version": "0.3.1"
+ },
+ "node-html-encoder": {
+ "version": "0.0.2"
+ },
+ "promises-aplus-tests": {
+ "version": "1.3.2",
+ "dependencies": {
+ "mocha": {
+ "version": "1.11.0",
+ "dependencies": {
+ "commander": {
+ "version": "0.6.1"
+ },
+ "growl": {
+ "version": "1.7.0"
+ },
+ "jade": {
+ "version": "0.26.3",
+ "dependencies": {
+ "mkdirp": {
+ "version": "0.3.0"
+ }
+ }
+ },
+ "diff": {
+ "version": "1.0.2"
+ },
+ "debug": {
+ "version": "0.7.4"
+ },
+ "mkdirp": {
+ "version": "0.3.5"
+ },
+ "ms": {
+ "version": "0.3.0"
+ },
+ "glob": {
+ "version": "3.2.1",
+ "dependencies": {
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "graceful-fs": {
+ "version": "1.2.3"
+ },
+ "inherits": {
+ "version": "1.0.0"
+ }
+ }
+ }
+ }
+ },
+ "sinon": {
+ "version": "1.7.3",
+ "dependencies": {
+ "buster-format": {
+ "version": "0.5.6",
+ "dependencies": {
+ "buster-core": {
+ "version": "0.6.4"
+ }
+ }
+ }
+ }
+ },
+ "underscore": {
+ "version": "1.4.4"
+ }
+ }
+ },
+ "protractor": {
+ "version": "0.19.0",
+ "dependencies": {
+ "selenium-webdriver": {
+ "version": "2.39.0"
+ },
+ "minijasminenode": {
+ "version": "0.2.7"
+ },
+ "saucelabs": {
+ "version": "0.1.1"
+ },
+ "glob": {
+ "version": "3.2.9",
+ "dependencies": {
+ "minimatch": {
+ "version": "0.2.14",
+ "dependencies": {
+ "lru-cache": {
+ "version": "2.5.0"
+ },
+ "sigmund": {
+ "version": "1.0.0"
+ }
+ }
+ },
+ "inherits": {
+ "version": "2.0.1"
+ }
+ }
+ },
+ "adm-zip": {
+ "version": "0.4.4"
+ },
+ "optimist": {
+ "version": "0.6.1",
+ "dependencies": {
+ "wordwrap": {
+ "version": "0.0.2"
+ },
+ "minimist": {
+ "version": "0.0.8"
+ }
+ }
+ }
+ }
+ },
+ "q": {
+ "version": "1.0.0"
+ },
+ "q-io": {
+ "version": "1.10.9",
+ "dependencies": {
+ "q": {
+ "version": "0.9.7"
+ },
+ "qs": {
+ "version": "0.1.0"
+ },
+ "url2": {
+ "version": "0.0.0"
+ },
+ "mime": {
+ "version": "1.2.11"
+ },
+ "mimeparse": {
+ "version": "0.1.4"
+ },
+ "collections": {
+ "version": "0.2.2",
+ "dependencies": {
+ "weak-map": {
+ "version": "1.0.0"
+ }
+ }
+ }
+ }
+ },
+ "qq": {
+ "version": "0.3.5",
+ "dependencies": {
+ "q": {
+ "version": "0.8.4"
+ }
+ }
+ },
+ "rewire": {
+ "version": "1.1.3"
+ },
+ "semver": {
+ "version": "2.1.0"
+ },
+ "shelljs": {
+ "version": "0.2.6"
+ },
+ "sorted-object": {
+ "version": "1.0.0"
+ },
+ "winston": {
+ "version": "0.7.2",
+ "dependencies": {
+ "async": {
+ "version": "0.2.10"
+ },
+ "colors": {
+ "version": "0.6.2"
+ },
+ "cycle": {
+ "version": "1.0.3"
+ },
+ "eyes": {
+ "version": "0.1.8"
+ },
+ "pkginfo": {
+ "version": "0.3.0"
+ },
+ "request": {
+ "version": "2.16.6",
+ "dependencies": {
+ "form-data": {
+ "version": "0.0.10",
+ "dependencies": {
+ "combined-stream": {
+ "version": "0.0.4",
+ "dependencies": {
+ "delayed-stream": {
+ "version": "0.0.5"
+ }
+ }
+ }
+ }
+ },
+ "mime": {
+ "version": "1.2.11"
+ },
+ "hawk": {
+ "version": "0.10.2",
+ "dependencies": {
+ "hoek": {
+ "version": "0.7.6"
+ },
+ "boom": {
+ "version": "0.3.8"
+ },
+ "cryptiles": {
+ "version": "0.1.3"
+ },
+ "sntp": {
+ "version": "0.1.4"
+ }
+ }
+ },
+ "node-uuid": {
+ "version": "1.4.1"
+ },
+ "cookie-jar": {
+ "version": "0.2.0"
+ },
+ "aws-sign": {
+ "version": "0.2.0"
+ },
+ "oauth-sign": {
+ "version": "0.2.0"
+ },
+ "forever-agent": {
+ "version": "0.2.0"
+ },
+ "tunnel-agent": {
+ "version": "0.2.0"
+ },
+ "json-stringify-safe": {
+ "version": "3.0.0"
+ },
+ "qs": {
+ "version": "0.5.6"
+ }
+ }
+ },
+ "stack-trace": {
+ "version": "0.0.9"
+ }
+ }
+ },
+ "yaml-js": {
+ "version": "0.0.8"
+ }
+ },
+ "name": "angularjs"
+}
diff --git a/package.json b/package.json
index 629b736007f8..ac7fab66c471 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,7 @@
"grunt-contrib-jshint": "~0.7.2",
"grunt-ddescribe-iit": "~0.0.1",
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
- "grunt-jscs-checker": "~0.3.2",
+ "grunt-jscs-checker": "~0.4.0",
"grunt-merge-conflict": "~0.0.1",
"grunt-parallel": "~0.3.1",
"grunt-shell": "~0.4.0",
@@ -27,7 +27,7 @@
"q-io": "~1.10.6",
"qq": "~0.3.5",
"shelljs": "~0.2.6",
- "karma": "0.11.12",
+ "karma": "^0.12.0",
"karma-jasmine": "0.1.5",
"karma-chrome-launcher": "0.1.2",
"karma-firefox-launcher": "0.1.3",
@@ -50,10 +50,12 @@
"gulp-concat": "~2.1.7",
"canonical-path": "0.0.2",
"winston": "~0.7.2",
- "dgeni": "~0.2.0",
- "dgeni-packages": "^0.3.0",
+ "dgeni": "^0.2.2",
+ "dgeni-packages": "^0.8.1",
"gulp-jshint": "~1.4.2",
- "jshint-stylish": "~0.1.5"
+ "jshint-stylish": "~0.1.5",
+ "node-html-encoder": "0.0.2",
+ "sorted-object": "^1.0.0"
},
"licenses": [
{
diff --git a/scripts/angularjs.org/publish.sh b/scripts/angularjs.org/publish.sh
index 1efb621b9012..1e59335d57d5 100755
--- a/scripts/angularjs.org/publish.sh
+++ b/scripts/angularjs.org/publish.sh
@@ -11,8 +11,11 @@ ARG_DEFS=(
)
function init {
- TMP_DIR=$(resolveDir ../../tmp)
+ BASE_DIR=$(resolveDir ../..)
+ TMP_DIR=$BASE_DIR/tmp
REPO_DIR=$TMP_DIR/angularjs.org
+ BRANCH_PATTERN=$(readJsonProp "$BASE_DIR/package.json" "branchVersion")
+ BUILD_DIR=$BASE_DIR/build
}
function prepare {
@@ -24,18 +27,24 @@ function prepare {
#
echo "-- Updating angularjs.org"
cd $REPO_DIR
- VERSION_REGEX="[a-z0-9\-\.\+]+"
+ VERSION_REGEX="[-a-z0-9\.\+]+"
- replaceInFile "index.html" "(ajax\/libs\/angularjs\/)$VERSION_REGEX" "\1$CDN_VERSION"
- replaceInFile "index.html" "([^<]*)$VERSION_REGEX" "\1$CDN_VERSION"
- replaceInFile "index.html" "(code.angularjs.org\/)$VERSION_REGEX" "\1$CDN_VERSION"
+ # Replace the version in the script links that reference the Google CDN
+ # e.g.
+ replaceInFile "index.html" "(http:\/\/ajax.googleapis.com\/ajax\/libs\/angularjs\/)$VERSION_REGEX" "\1$CDN_VERSION"
- replaceInFile "js/homepage.js" "($scope.CURRENT_STABLE_VERSION[ ]*=[ ]*')$VERSION_REGEX" "\1$CDN_VERSION"
- replaceInFile "js/homepage.js" "($scope.CURRENT_UNSTABLE_VERSION[ ]*=[ ]*')$VERSION_REGEX" "\1$CDN_VERSION"
+ # Replace the version in the script links that reference code.angularjs.org
+ # e.g.
+ replaceInFile "index.html" "(code\.angularjs\.org\/)$VERSION_REGEX" "\1$CDN_VERSION"
+
+ # Replace the version of the branch that we are updating
+ echo $BRANCH_PATTERN
+ echo $CDN_VERSION
+ replaceInFile "js/download-data.js" "branch:[ ]+'($BRANCH_PATTERN)',[ ]+version:[ ]+'$VERSION_REGEX'" "branch: '\1', version: '$CDN_VERSION'"
git add index.html
- git add js/homepage.js
- git commit -m "update(version): update angular version to $CDN_VERSION"
+ git add js/download-data.js
+ git commit -m "update(version): update angular version to $CDN_VERSION for branch $BRANCH_PATTERN"
}
function publish {
diff --git a/scripts/clean-shrinkwrap.js b/scripts/clean-shrinkwrap.js
new file mode 100755
index 000000000000..f3d6ebe587a1
--- /dev/null
+++ b/scripts/clean-shrinkwrap.js
@@ -0,0 +1,45 @@
+#!/usr/bin/env node
+
+/**
+ * this script is just a temporary solution to deal with the issue of npm outputting the npm
+ * shrinkwrap file in an unstable manner.
+ *
+ * See: https://github.com/npm/npm/issues/3581
+ */
+
+var _ = require('lodash');
+var sorted = require('sorted-object');
+var fs = require('fs');
+
+
+function cleanModule(module, name) {
+
+ // keep `from` and `resolve` properties for git dependencies, delete otherwise
+ if (!(module.resolved && module.resolved.match(/^git:\/\//))) {
+ delete module.from;
+ delete module.resolved;
+ }
+
+ if (name === 'chokidar') {
+ if (module.version === '0.8.1') {
+ delete module.dependencies;
+ } else {
+ throw new Error("Unfamiliar chokidar version (v" + module.version +
+ ") , please check status of https://github.com/paulmillr/chokidar/pull/106");
+ }
+ }
+
+ _.forEach(module.dependencies, function(mod, name) {
+ cleanModule(mod, name);
+ });
+}
+
+
+console.log('Reading npm-shrinkwrap.json');
+var shrinkwrap = require('./../npm-shrinkwrap.json');
+
+console.log('Cleaning shrinkwrap object');
+cleanModule(shrinkwrap, shrinkwrap.name);
+
+console.log('Writing cleaned npm-shrinkwrap.json');
+fs.writeFileSync('./npm-shrinkwrap.json', JSON.stringify(sorted(shrinkwrap), null, 2) + "\n");
diff --git a/scripts/code.angularjs.org/publish.sh b/scripts/code.angularjs.org/publish.sh
index 4845ad2ace70..fdbadf3b7908 100755
--- a/scripts/code.angularjs.org/publish.sh
+++ b/scripts/code.angularjs.org/publish.sh
@@ -55,19 +55,37 @@ function prepare {
git commit -m "v$NEW_VERSION"
}
-function publish {
- if [[ $IS_SNAPSHOT_BUILD ]]; then
- echo "-- Updating snapshot version"
- curl -G --data-urlencode "ver=$NEW_VERSION" http://code.angularjs.org/fetchLatestSnapshot.php
- exit 0;
- fi
+function _update_snapshot() {
+ for backend in "$@" ; do
+ echo "-- Updating snapshot version: backend=$backend"
+ curl -G --data-urlencode "ver=$NEW_VERSION" http://$backend:8003/fetchLatestSnapshot.php
+ done
+}
+
+function _update_code() {
cd $REPO_DIR
+
echo "-- Pushing code.angularjs.org"
git push origin master
- echo "-- Refreshing code.angularjs.org"
- curl http://code.angularjs.org/gitFetchSite.php
+ for backend in "$@" ; do
+ echo "-- Refreshing code.angularjs.org: backend=$backend"
+ curl http://$backend:8003/gitFetchSite.php
+ done
+}
+
+function publish {
+ # The TXT record for backends.angularjs.org is a CSV of the IP addresses for
+ # the currently serving Compute Engine backends.
+ # code.angularjs.org is served out of port 8003 on these backends.
+ backends=("$(dig backends.angularjs.org +short TXT | python -c 'print raw_input()[1:-1].replace(",", "\n")')")
+
+ if [[ $IS_SNAPSHOT_BUILD ]]; then
+ _update_snapshot ${backends[@]}
+ else
+ _update_code ${backends[@]}
+ fi
}
source $(dirname $0)/../utils.inc
diff --git a/scripts/jenkins/release.sh b/scripts/jenkins/release.sh
index 8c1e46bc09b0..bb512f7030bb 100755
--- a/scripts/jenkins/release.sh
+++ b/scripts/jenkins/release.sh
@@ -23,7 +23,7 @@ ARG_DEFS=(
)
function init {
- if [[ $(git rev-parse --short HEAD) != $COMMIT_SHA ]]; then
+ if [[ $(git rev-parse HEAD) != $(git rev-parse $COMMIT_SHA) ]]; then
echo "HEAD is not at $COMMIT_SHA"
usage
fi
diff --git a/scripts/utils.inc b/scripts/utils.inc
index 71c3bc9bfc75..919f7df9799e 100644
--- a/scripts/utils.inc
+++ b/scripts/utils.inc
@@ -15,7 +15,7 @@
# 0. Set the current directory to the directory of the script. By this
# the script can be called from anywhere.
# 1. Parse the named arguments
-# 2. If the parameter "git_push_dryrun" is set, all calls the `git push` in this script
+# 2. If the parameter "git_push_dryrun" is set, all calls to `git push` in this script
# or in child scripts will be intercepted so that the `--dry-run` and `--porcelain` is added
# to show what the push would do but not actually do it.
# 3. If the parameter "verbose" is set, the `-x` flag will be set in bash.
@@ -36,7 +36,7 @@
# with the name of the parameter in upper case (with dash converted to underscore).
#
# Special arguments that are always available:
-# - "--action=.*": This parameter will be used to dispatch to a function with that name when the
+# - "--action=.*": This parameter will be used to execute a function with that name when the
# script is started
# - "--git_push_dryrun=true": This will intercept all calls to `git push` in this script
# or in child scripts so that the `--dry-run` and `--porcelain` is added
@@ -195,7 +195,7 @@ function isFunction {
}
# readJsonProp(jsonFile, property)
-# - restriction: property needs to be on an own line!
+# - restriction: property needs to be on a single line!
function readJsonProp {
echo $(sed -En 's/.*"'$2'"[ ]*:[ ]*"(.*)".*/\1/p' $1)
}
diff --git a/src/.jshintrc b/src/.jshintrc
index f32caa451ed6..35bba32e73ed 100644
--- a/src/.jshintrc
+++ b/src/.jshintrc
@@ -64,6 +64,7 @@
"isWindow": false,
"isScope": false,
"isFile": false,
+ "isBlob": false,
"isBoolean": false,
"trim": false,
"isElement": false,
diff --git a/src/Angular.js b/src/Angular.js
index 68ae929539ce..ec69cc1a59a1 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -45,6 +45,7 @@
-isWindow,
-isScope,
-isFile,
+ -isBlob,
-isBoolean,
-trim,
-isElement,
@@ -566,6 +567,11 @@ function isFile(obj) {
}
+function isBlob(obj) {
+ return toString.call(obj) === '[object Blob]';
+}
+
+
function isBoolean(value) {
return typeof value === 'boolean';
}
@@ -1235,6 +1241,41 @@ function angularInit(element, bootstrap) {
* Note that ngScenario-based end-to-end tests cannot use this function to bootstrap manually.
* They must use {@link ng.directive:ngApp ngApp}.
*
+ * Angular will detect if it has been loaded into the browser more than once and only allow the
+ * first loaded script to be bootstrapped and will report a warning to the browser console for
+ * each of the subsequent scripts. This prevents strange results in applications, where otherwise
+ * multiple instances of Angular try to work on the DOM.
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * {{heading}}
+ *
+ *
+ * {{fill}}
+ *
+ *
+ *
+ *
+ *
+ * var app = angular.module('multi-bootstrap', [])
+ *
+ * .controller('BrokenTable', function($scope) {
+ * $scope.headings = ['One', 'Two', 'Three'];
+ * $scope.fillings = [[1, 2, 3], ['A', 'B', 'C'], [7, 8, 9]];
+ * });
+ *
+ *
+ * it('should only insert one table cell for each item in $scope.fillings', function() {
+ * expect(element.all(by.css('td')).count())
+ * .toBe(9);
+ * });
+ *
+ *
+ *
* @param {Element} element DOM element which is the root of angular application.
* @param {Array=} modules an array of modules to load into the application.
* Each item in the array should be the name of a predefined module or a (DI annotated)
diff --git a/src/angular.suffix b/src/angular.suffix
index c86200bb31f4..9429d4fcf3d2 100644
--- a/src/angular.suffix
+++ b/src/angular.suffix
@@ -1,3 +1,9 @@
+ if (window.angular.bootstrap) {
+ //AngularJS is already loaded, so we can return here...
+ console.log('WARNING: Tried to load angular more than once.');
+ return;
+ }
+
//try to bind to jquery now so that one can write angular.element().read()
//but we will rebind on bootstrap again.
bindJQuery();
diff --git a/src/jqLite.js b/src/jqLite.js
index 809ede034a00..738f47a9b167 100644
--- a/src/jqLite.js
+++ b/src/jqLite.js
@@ -364,11 +364,15 @@ function jqLiteInheritedData(element, name, value) {
var names = isArray(name) ? name : [name];
while (element.length) {
-
+ var node = element[0];
for (var i = 0, ii = names.length; i < ii; i++) {
if ((value = element.data(names[i])) !== undefined) return value;
}
- element = element.parent();
+
+ // If dealing with a document fragment node with a host element, and no parent, use the host
+ // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
+ // to lookup parent controllers.
+ element = jqLite(node.parentNode || (node.nodeType === 11 && node.host));
}
}
@@ -457,7 +461,7 @@ forEach({
return jqLite(element).data('$isolateScope') || jqLite(element).data('$isolateScopeNoTemplate');
},
- controller: jqLiteController ,
+ controller: jqLiteController,
injector: function(element) {
return jqLiteInheritedData(element, '$injector');
diff --git a/src/loader.js b/src/loader.js
index 08d285c32eaa..32ce08ee0c28 100644
--- a/src/loader.js
+++ b/src/loader.js
@@ -55,10 +55,10 @@ function setupModuleLoader(window) {
* myModule.value('appName', 'MyCoolApp');
*
* // configure existing services inside initialization blocks.
- * myModule.config(function($locationProvider) {
+ * myModule.config(['$locationProvider', function($locationProvider) {
* // Configure existing providers
* $locationProvider.hashPrefix('!');
- * });
+ * }]);
* ```
*
* Then you can create an injector and load your modules like this:
@@ -114,7 +114,6 @@ function setupModuleLoader(window) {
* @ngdoc property
* @name angular.Module#requires
* @module ng
- * @propertyOf angular.Module
* @returns {Array.} List of module names which must be loaded before this module.
* @description
* Holds the list of modules which the injector will load before the current module is
@@ -126,7 +125,6 @@ function setupModuleLoader(window) {
* @ngdoc property
* @name angular.Module#name
* @module ng
- * @propertyOf angular.Module
* @returns {string} Name of the module.
* @description
*/
diff --git a/src/ng/browser.js b/src/ng/browser.js
index 6bec52805722..f9502cd4a09b 100644
--- a/src/ng/browser.js
+++ b/src/ng/browser.js
@@ -194,7 +194,6 @@ function Browser(window, document, $log, $sniffer) {
/**
* @name $browser#onUrlChange
- * @TODO(vojta): refactor to use node's syntax for events
*
* @description
* Register callback function that will be called, when url changes.
@@ -215,6 +214,7 @@ function Browser(window, document, $log, $sniffer) {
* @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
*/
self.onUrlChange = function(callback) {
+ // TODO(vojta): refactor to use node's syntax for events
if (!urlChangeInit) {
// We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
// don't fire popstate when user change the address bar and don't fire hashchange when url
diff --git a/src/ng/cacheFactory.js b/src/ng/cacheFactory.js
index c16c1e1f0b4e..69c4ed93a0e0 100644
--- a/src/ng/cacheFactory.js
+++ b/src/ng/cacheFactory.js
@@ -208,15 +208,11 @@ function $CacheFactoryProvider() {
* `$templateCache` service directly.
*
* Adding via the `script` tag:
+ *
* ```html
- *
- *
- *
- *
- * ...
- *
+ *
* ```
*
* **Note:** the `script` tag containing the template does not need to be included in the `head` of
diff --git a/src/ng/compile.js b/src/ng/compile.js
index 5b625c193478..c7cd08bce127 100644
--- a/src/ng/compile.js
+++ b/src/ng/compile.js
@@ -503,7 +503,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
Suffix = 'Directive',
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
- TABLE_CONTENT_REGEXP = /^<\s*(tr|th|td|tbody)(\s+[^>]*)?>/i;
+ TABLE_CONTENT_REGEXP = /^<\s*(tr|th|td|thead|tbody|tfoot)(\s+[^>]*)?>/i;
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
// The assumption is that future DOM event attribute names will begin with
@@ -1649,16 +1649,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
template = trim(template);
if ((type = TABLE_CONTENT_REGEXP.exec(template))) {
type = type[1].toLowerCase();
- var table = jqLite(''),
- tbody = table.children('tbody'),
- leaf = /(td|th)/.test(type) && table.find('tr');
- if (tbody.length && type !== 'tbody') {
- table = tbody;
+ var table = jqLite('');
+ if (/(thead|tbody|tfoot)/.test(type)) {
+ return table.children(type);
}
- if (leaf && leaf.length) {
- table = leaf;
+ table = table.children('tbody');
+ if (type === 'tr') {
+ return table.children('tr');
}
- return table.contents();
+ return table.children('tr').contents();
}
return jqLite('' +
template +
diff --git a/src/ng/directive/booleanAttrs.js b/src/ng/directive/booleanAttrs.js
index 50f118d484af..e98a61034063 100644
--- a/src/ng/directive/booleanAttrs.js
+++ b/src/ng/directive/booleanAttrs.js
@@ -277,7 +277,7 @@
* such as selected. (Their presence means true and their absence means false.)
* If we put an Angular interpolation expression into such an attribute then the
* binding information would be lost when the browser removes the attribute.
- * The `ngSelected` directive solves this problem for the `selected` atttribute.
+ * The `ngSelected` directive solves this problem for the `selected` attribute.
* This complementary directive is not removed by the browser and so provides
* a permanent reliable place to store the binding information.
*
diff --git a/src/ng/directive/form.js b/src/ng/directive/form.js
index 1424157c8751..9983d367f949 100644
--- a/src/ng/directive/form.js
+++ b/src/ng/directive/form.js
@@ -360,8 +360,6 @@ function FormController(element, attrs, $scope, $animate) {
*
- * @param {string=} name Name of the form. If specified, the form controller will be published into
- * related scope, under this name.
*/
var formDirectiveFactory = function(isNgForm) {
return ['$timeout', function($timeout) {
diff --git a/src/ng/directive/input.js b/src/ng/directive/input.js
index c31bb4004947..afdb0bfadfba 100644
--- a/src/ng/directive/input.js
+++ b/src/ng/directive/input.js
@@ -510,7 +510,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
timeout = $browser.defer(function() {
listener();
timeout = null;
- });
+ },5); // Adding 5 milliseconds delay for iOS7
}
};
diff --git a/src/ng/directive/ngBind.js b/src/ng/directive/ngBind.js
index ba4968d1311b..d2a9c2f1b21a 100644
--- a/src/ng/directive/ngBind.js
+++ b/src/ng/directive/ngBind.js
@@ -13,7 +13,7 @@
* Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
* `{{ expression }}` which is similar but less verbose.
*
- * It is preferrable to use `ngBind` instead of `{{ expression }}` when a template is momentarily
+ * It is preferable to use `ngBind` instead of `{{ expression }}` when a template is momentarily
* displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
* element attribute, it makes the bindings invisible to the user while the page is loading.
*
diff --git a/src/ng/directive/ngInclude.js b/src/ng/directive/ngInclude.js
index 272e199ab210..38a9e00568b4 100644
--- a/src/ng/directive/ngInclude.js
+++ b/src/ng/directive/ngInclude.js
@@ -32,7 +32,7 @@
* @priority 400
*
* @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
- * make sure you wrap it in quotes, e.g. `src="'myPartialTemplate.html'"`.
+ * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
* @param {string=} onload Expression to evaluate when a new partial is loaded.
*
* @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
@@ -146,7 +146,6 @@
/**
* @ngdoc event
* @name ngInclude#$includeContentRequested
- * @eventOf ng.directive:ngInclude
* @eventType emit on the scope ngInclude was declared in
* @description
* Emitted every time the ngInclude content is requested.
@@ -156,7 +155,6 @@
/**
* @ngdoc event
* @name ngInclude#$includeContentLoaded
- * @eventOf ng.directive:ngInclude
* @eventType emit on the current ngInclude scope
* @description
* Emitted every time the ngInclude content is reloaded.
diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js
index c6a9bda3f48a..11f35c56e84c 100644
--- a/src/ng/directive/ngRepeat.js
+++ b/src/ng/directive/ngRepeat.js
@@ -68,9 +68,11 @@
* as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
*
* @animations
- * enter - when a new item is added to the list or when an item is revealed after a filter
- * leave - when an item is removed from the list or when an item is filtered out
- * move - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
+ * **.enter** - when a new item is added to the list or when an item is revealed after a filter
+ *
+ * **.leave** - when an item is removed from the list or when an item is filtered out
+ *
+ * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
*
* @element ANY
* @scope
diff --git a/src/ng/directive/ngSwitch.js b/src/ng/directive/ngSwitch.js
index 378c0b5024fc..021f4c7d4d7f 100644
--- a/src/ng/directive/ngSwitch.js
+++ b/src/ng/directive/ngSwitch.js
@@ -41,7 +41,6 @@
* @scope
* @priority 800
* @param {*} ngSwitch|on expression to match against
ng-switch-when .
- * @paramDescription
* On child elements add:
*
* * `ngSwitchWhen`: the case statement to match against. If match then this
diff --git a/src/ng/directive/select.js b/src/ng/directive/select.js
index 0b562cca686c..628d21776b85 100644
--- a/src/ng/directive/select.js
+++ b/src/ng/directive/select.js
@@ -394,6 +394,12 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
value = valueFn(scope, locals);
}
}
+ // Update the null option's selected property here so $render cleans it up correctly
+ if (optionGroupsCache[0].length > 1) {
+ if (optionGroupsCache[0][1].id !== key) {
+ optionGroupsCache[0][1].selected = false;
+ }
+ }
}
ctrl.$setViewValue(value);
});
@@ -531,7 +537,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
lastElement.val(existingOption.id = option.id);
}
// lastElement.prop('selected') provided by jQuery has side-effects
- if (lastElement[0].selected !== option.selected) {
+ if (existingOption.selected !== option.selected) {
lastElement.prop('selected', (existingOption.selected = option.selected));
}
} else {
diff --git a/src/ng/document.js b/src/ng/document.js
index cc7604773d61..321a36520805 100644
--- a/src/ng/document.js
+++ b/src/ng/document.js
@@ -7,6 +7,22 @@
*
* @description
* A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
+ *
+ * @example
+
+
+
+
$document title:
+
window.document title:
+
+
+
+ function MainCtrl($scope, $document) {
+ $scope.title = $document[0].title;
+ $scope.windowTitle = angular.element(window.document)[0].title;
+ }
+
+
*/
function $DocumentProvider(){
this.$get = ['$window', function(window){
diff --git a/src/ng/filter/filters.js b/src/ng/filter/filters.js
index b81838c68041..6f9cc48b702f 100644
--- a/src/ng/filter/filters.js
+++ b/src/ng/filter/filters.js
@@ -441,7 +441,7 @@ function dateFilter($locale) {
* @returns {string} JSON string.
*
*
- * @example:
+ * @example
{{ {'name':'value'} | json }}
diff --git a/src/ng/filter/orderBy.js b/src/ng/filter/orderBy.js
index b62626988fc5..faeb8ed1e570 100644
--- a/src/ng/filter/orderBy.js
+++ b/src/ng/filter/orderBy.js
@@ -74,6 +74,12 @@ function orderByFilter($parse){
predicate = predicate.substring(1);
}
get = $parse(predicate);
+ if (get.constant) {
+ var key = get();
+ return reverseComparator(function(a,b) {
+ return compare(a[key], b[key]);
+ }, descending);
+ }
}
return reverseComparator(function(a,b){
return compare(get(a),get(b));
diff --git a/src/ng/http.js b/src/ng/http.js
index 0c54f5bb636e..9fcb45782f87 100644
--- a/src/ng/http.js
+++ b/src/ng/http.js
@@ -103,7 +103,7 @@ function $HttpProvider() {
// transform outgoing request data
transformRequest: [function(d) {
- return isObject(d) && !isFile(d) ? toJson(d) : d;
+ return isObject(d) && !isFile(d) && !isBlob(d) ? toJson(d) : d;
}],
// default headers
@@ -236,9 +236,8 @@ function $HttpProvider() {
*
* # Shortcut methods
*
- * Since all invocations of the $http service require passing in an HTTP method and URL, and
- * POST/PUT requests require request data to be provided as well, shortcut methods
- * were created:
+ * Shortcut methods are also available. All shortcut methods require passing in the URL, and
+ * request data must be passed in for POST/PUT requests.
*
* ```js
* $http.get('/someUrl').success(successCallback);
diff --git a/src/ng/httpBackend.js b/src/ng/httpBackend.js
index ee2a37fe10a8..28107966f643 100644
--- a/src/ng/httpBackend.js
+++ b/src/ng/httpBackend.js
@@ -144,9 +144,11 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
jsonpDone = xhr = null;
// fix status code when it is 0 (0 status is undocumented).
- // Occurs when accessing file resources.
- // On Android 4.1 stock browser it occurs while retrieving files from application cache.
- status = (status === 0) ? (response ? 200 : 404) : status;
+ // Occurs when accessing file resources or on Android 4.1 stock browser
+ // while retrieving files from application cache.
+ if (status === 0) {
+ status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
+ }
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
status = status == 1223 ? 204 : status;
diff --git a/src/ng/q.js b/src/ng/q.js
index eb3b2beda80a..76e00df02812 100644
--- a/src/ng/q.js
+++ b/src/ng/q.js
@@ -112,7 +112,7 @@
*
* Because `finally` is a reserved word in JavaScript and reserved keywords are not supported as
* property names by ES3, you'll need to invoke the method like `promise['finally'](callback)` to
- * make your code IE8 compatible.
+ * make your code IE8 and Android 2.x compatible.
*
* # Chaining promises
*
diff --git a/src/ng/raf.js b/src/ng/raf.js
index f85ee12a4223..bb1f47ed5e65 100644
--- a/src/ng/raf.js
+++ b/src/ng/raf.js
@@ -1,21 +1,32 @@
'use strict';
function $$RAFProvider(){ //rAF
- this.$get = ['$window', function($window) {
+ this.$get = ['$window', '$timeout', function($window, $timeout) {
var requestAnimationFrame = $window.requestAnimationFrame ||
- $window.webkitRequestAnimationFrame;
+ $window.webkitRequestAnimationFrame ||
+ $window.mozRequestAnimationFrame;
var cancelAnimationFrame = $window.cancelAnimationFrame ||
- $window.webkitCancelAnimationFrame;
+ $window.webkitCancelAnimationFrame ||
+ $window.mozCancelAnimationFrame ||
+ $window.webkitCancelRequestAnimationFrame;
- var raf = function(fn) {
- var id = requestAnimationFrame(fn);
- return function() {
- cancelAnimationFrame(id);
- };
- };
+ var rafSupported = !!requestAnimationFrame;
+ var raf = rafSupported
+ ? function(fn) {
+ var id = requestAnimationFrame(fn);
+ return function() {
+ cancelAnimationFrame(id);
+ };
+ }
+ : function(fn) {
+ var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
+ return function() {
+ $timeout.cancel(timer);
+ };
+ };
- raf.supported = !!requestAnimationFrame;
+ raf.supported = rafSupported;
return raf;
}];
diff --git a/src/ng/rootScope.js b/src/ng/rootScope.js
index 9c75534c7145..dbb93000cd72 100644
--- a/src/ng/rootScope.js
+++ b/src/ng/rootScope.js
@@ -139,7 +139,6 @@ function $RootScopeProvider(){
/**
* @ngdoc property
* @name $rootScope.Scope#$id
- * @propertyOf ng.$rootScope.Scope
* @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for
* debugging.
*/
@@ -399,30 +398,40 @@ function $RootScopeProvider(){
* {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
* collection will trigger a call to the `listener`.
*
- * @param {function(newCollection, oldCollection, scope)} listener a callback function that is
- * fired with both the `newCollection` and `oldCollection` as parameters.
- * The `newCollection` object is the newly modified data obtained from the `obj` expression
- * and the `oldCollection` object is a copy of the former collection data.
- * The `scope` refers to the current scope.
+ * @param {function(newCollection, oldCollection, scope)} listener a callback function called
+ * when a change is detected.
+ * - The `newCollection` object is the newly modified data obtained from the `obj` expression
+ * - The `oldCollection` object is a copy of the former collection data.
+ * Due to performance considerations, the`oldCollection` value is computed only if the
+ * `listener` function declares two or more arguments.
+ * - The `scope` argument refers to the current scope.
*
* @returns {function()} Returns a de-registration function for this listener. When the
* de-registration function is executed, the internal watch operation is terminated.
*/
$watchCollection: function(obj, listener) {
var self = this;
- var oldValue;
+ // the current value, updated on each dirty-check run
var newValue;
+ // a shallow copy of the newValue from the last dirty-check run,
+ // updated to match newValue during dirty-check run
+ var oldValue;
+ // a shallow copy of the newValue from when the last change happened
+ var veryOldValue;
+ // only track veryOldValue if the listener is asking for it
+ var trackVeryOldValue = (listener.length > 1);
var changeDetected = 0;
var objGetter = $parse(obj);
var internalArray = [];
var internalObject = {};
+ var initRun = true;
var oldLength = 0;
function $watchCollectionWatch() {
newValue = objGetter(self);
var newLength, key;
- if (!isObject(newValue)) {
+ if (!isObject(newValue)) { // if primitive
if (oldValue !== newValue) {
oldValue = newValue;
changeDetected++;
@@ -444,7 +453,9 @@ function $RootScopeProvider(){
}
// copy the items to oldValue and look for changes.
for (var i = 0; i < newLength; i++) {
- if (oldValue[i] !== newValue[i]) {
+ var bothNaN = (oldValue[i] !== oldValue[i]) &&
+ (newValue[i] !== newValue[i]);
+ if (!bothNaN && (oldValue[i] !== newValue[i])) {
changeDetected++;
oldValue[i] = newValue[i];
}
@@ -488,7 +499,32 @@ function $RootScopeProvider(){
}
function $watchCollectionAction() {
- listener(newValue, oldValue, self);
+ if (initRun) {
+ initRun = false;
+ listener(newValue, newValue, self);
+ } else {
+ listener(newValue, veryOldValue, self);
+ }
+
+ // make a copy for the next time a collection is changed
+ if (trackVeryOldValue) {
+ if (!isObject(newValue)) {
+ //primitive
+ veryOldValue = newValue;
+ } else if (isArrayLike(newValue)) {
+ veryOldValue = new Array(newValue.length);
+ for (var i = 0; i < newValue.length; i++) {
+ veryOldValue[i] = newValue[i];
+ }
+ } else { // if object
+ veryOldValue = {};
+ for (var key in newValue) {
+ if (hasOwnProperty.call(newValue, key)) {
+ veryOldValue[key] = newValue[key];
+ }
+ }
+ }
+ }
}
return this.$watch($watchCollectionWatch, $watchCollectionAction);
@@ -652,7 +688,6 @@ function $RootScopeProvider(){
/**
* @ngdoc event
* @name $rootScope.Scope#$destroy
- * @eventOf ng.$rootScope.Scope
* @eventType broadcast on scope being destroyed
*
* @description
diff --git a/src/ng/sce.js b/src/ng/sce.js
index 612edd1222a0..0f60521b26bd 100644
--- a/src/ng/sce.js
+++ b/src/ng/sce.js
@@ -521,7 +521,7 @@ function $SceDelegateProvider() {
* |---------------------|----------------|
* | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. |
* | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
- * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (` Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
* | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. |
*
diff --git a/src/ng/sniffer.js b/src/ng/sniffer.js
index c225ab8c1ff2..b937cee72edb 100644
--- a/src/ng/sniffer.js
+++ b/src/ng/sniffer.js
@@ -21,6 +21,8 @@ function $SnifferProvider() {
android =
int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
+ webkit =
+ int((/[a-z]*?webkit\/(\d+)/i.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
document = $document[0] || {},
documentMode = document.documentMode,
vendorPrefix,
@@ -62,8 +64,10 @@ function $SnifferProvider() {
// older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
// so let's not use the history API also
// We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
+ // older webkit browsers (<= 533.x) have problem with the history state, ie. Safari 5.0.2
+ // so check if webkit isn't lower than 534
// jshint -W018
- history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
+ history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee && !(webkit < 534)),
// jshint +W018
hashchange: 'onhashchange' in $window &&
// IE8 compatible mode lies
diff --git a/src/ngAnimate/animate.js b/src/ngAnimate/animate.js
index 6cc633a30107..542b678f8316 100644
--- a/src/ngAnimate/animate.js
+++ b/src/ngAnimate/animate.js
@@ -701,8 +701,7 @@ angular.module('ngAnimate', ['ng'])
/**
*
* @ngdoc function
- * @name ng.$animate#setClass
- * @methodOf ng.$animate
+ * @name $animate#setClass
* @function
* @description Adds and/or removes the given CSS classes to and from the element.
* Once complete, the done() callback will be fired (if provided).
@@ -771,7 +770,7 @@ angular.module('ngAnimate', ['ng'])
fireDOMOperation();
fireBeforeCallbackAsync();
fireAfterCallbackAsync();
- fireDoneCallbackAsync();
+ closeAnimation();
return;
}
@@ -950,7 +949,7 @@ angular.module('ngAnimate', ['ng'])
animation, but class-based animations don't. An example of this
failing would be when a parent HTML tag has a ng-class attribute
causing ALL directives below to skip animations during the digest */
- if(runner.isClassBased) {
+ if(runner && runner.isClassBased) {
cleanup(element, className);
} else {
$$asyncCallback(function() {
diff --git a/src/ngCookies/cookies.js b/src/ngCookies/cookies.js
index b78fcd5aaf8b..4f97c8dd17f3 100644
--- a/src/ngCookies/cookies.js
+++ b/src/ngCookies/cookies.js
@@ -25,8 +25,9 @@ angular.module('ngCookies', ['ng']).
* @description
* Provides read/write access to browser's cookies.
*
- * Only a simple Object is exposed and by adding or removing properties to/from
- * this object, new cookies are created/deleted at the end of current $eval.
+ * Only a simple Object is exposed and by adding or removing properties to/from this object, new
+ * cookies are created/deleted at the end of current $eval.
+ * The object's properties can only be strings.
*
* Requires the {@link ngCookies `ngCookies`} module to be installed.
*
@@ -94,12 +95,10 @@ angular.module('ngCookies', ['ng']).
for(name in cookies) {
value = cookies[name];
if (!angular.isString(value)) {
- if (angular.isDefined(lastCookies[name])) {
- cookies[name] = lastCookies[name];
- } else {
- delete cookies[name];
- }
- } else if (value !== lastCookies[name]) {
+ value = '' + value;
+ cookies[name] = value;
+ }
+ if (value !== lastCookies[name]) {
$browser.cookies(name, value);
updated = true;
}
diff --git a/src/ngMock/angular-mocks.js b/src/ngMock/angular-mocks.js
index 6a60d9bbf49d..67decaceb33f 100644
--- a/src/ngMock/angular-mocks.js
+++ b/src/ngMock/angular-mocks.js
@@ -1821,8 +1821,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
* an array containing response status (number), response data (string) and response headers
* (Object).
* - passThrough – `{function()}` – Any request matching a backend definition with `passThrough`
- * handler, will be pass through to the real backend (an XHR request will be made to the
- * server.
+ * handler will be passed through to the real backend (an XHR request will be made to the
+ * server.)
*/
/**
diff --git a/src/ngResource/resource.js b/src/ngResource/resource.js
index 66433736b3c7..8927a2664c16 100644
--- a/src/ngResource/resource.js
+++ b/src/ngResource/resource.js
@@ -253,7 +253,7 @@ function shallowClearAndCopy(src, dst) {
```js
var User = $resource('/user/:userId', {userId:'@id'});
- var user = User.get({userId:123}, function() {
+ User.get({userId:123}, function(user) {
user.abc = true;
user.$save();
});
@@ -273,6 +273,16 @@ function shallowClearAndCopy(src, dst) {
});
});
```
+ *
+ * You can also access the raw `$http` promise via the `$promise` property on the object returned
+ *
+ ```
+ var User = $resource('/user/:userId', {userId:'@id'});
+ User.get({userId:123})
+ .$promise.then(function(user) {
+ $scope.user = user;
+ });
+ ```
* # Creating a custom 'PUT' request
* In this example we create a custom method on our resource to make a PUT request
diff --git a/src/ngRoute/directive/ngView.js b/src/ngRoute/directive/ngView.js
index a25563ae38bc..85049d4a3163 100644
--- a/src/ngRoute/directive/ngView.js
+++ b/src/ngRoute/directive/ngView.js
@@ -119,38 +119,39 @@ ngRouteModule.directive('ngView', ngViewFillContentFactory);
- angular.module('ngViewExample', ['ngRoute', 'ngAnimate'],
- function($routeProvider, $locationProvider) {
- $routeProvider.when('/Book/:bookId', {
- templateUrl: 'book.html',
- controller: BookCtrl,
- controllerAs: 'book'
- });
- $routeProvider.when('/Book/:bookId/ch/:chapterId', {
- templateUrl: 'chapter.html',
- controller: ChapterCtrl,
- controllerAs: 'chapter'
- });
+ angular.module('ngViewExample', ['ngRoute', 'ngAnimate'])
+ .config(['$routeProvider', '$locationProvider',
+ function($routeProvider, $locationProvider) {
+ $routeProvider
+ .when('/Book/:bookId', {
+ templateUrl: 'book.html',
+ controller: 'BookCtrl',
+ controllerAs: 'book'
+ })
+ .when('/Book/:bookId/ch/:chapterId', {
+ templateUrl: 'chapter.html',
+ controller: 'ChapterCtrl',
+ controllerAs: 'chapter'
+ });
+
+ // configure html5 to get links working on jsfiddle
+ $locationProvider.html5Mode(true);
+ }])
+ .controller('MainCtrl', ['$route', '$routeParams', '$location',
+ function($route, $routeParams, $location) {
+ this.$route = $route;
+ this.$location = $location;
+ this.$routeParams = $routeParams;
+ }])
+ .controller('BookCtrl', ['$routeParams', function($routeParams) {
+ this.name = "BookCtrl";
+ this.params = $routeParams;
+ }])
+ .controller('ChapterCtrl', ['$routeParams', function($routeParams) {
+ this.name = "ChapterCtrl";
+ this.params = $routeParams;
+ }]);
- // configure html5 to get links working on jsfiddle
- $locationProvider.html5Mode(true);
- });
-
- function MainCtrl($route, $routeParams, $location) {
- this.$route = $route;
- this.$location = $location;
- this.$routeParams = $routeParams;
- }
-
- function BookCtrl($routeParams) {
- this.name = "BookCtrl";
- this.params = $routeParams;
- }
-
- function ChapterCtrl($routeParams) {
- this.name = "ChapterCtrl";
- this.params = $routeParams;
- }
diff --git a/src/ngRoute/route.js b/src/ngRoute/route.js
index a1236ec9341b..0f302e37b080 100644
--- a/src/ngRoute/route.js
+++ b/src/ngRoute/route.js
@@ -82,7 +82,7 @@ function $RouteProvider(){
*
* If `template` is a function, it will be called with the following parameters:
*
- * - `{Array.<Object>}` - route parameters extracted from the current
+ * - `{Array.}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
@@ -90,7 +90,7 @@ function $RouteProvider(){
*
* If `templateUrl` is a function, it will be called with the following parameters:
*
- * - `{Array.<Object>}` - route parameters extracted from the current
+ * - `{Array.}` - route parameters extracted from the current
* `$location.path()` by applying the current route
*
* - `resolve` - `{Object.=}` - An optional map of dependencies which should
@@ -247,7 +247,7 @@ function $RouteProvider(){
* - `$scope` - The current route scope.
* - `$template` - The current route template HTML.
*
- * @property {Array.} routes Array of all configured routes.
+ * @property {Object} routes Object with all route configuration Objects as its properties.
*
* @description
* `$route` is used for deep-linking URLs to controllers and views (HTML partials).
@@ -262,102 +262,106 @@ function $RouteProvider(){
* {@link ngRoute.$routeParams `$routeParams`} service.
*
* @example
- This example shows how changing the URL hash causes the `$route` to match a route against the
- URL, and the `ngView` pulls in the partial.
-
- Note that this example is using {@link ng.directive:script inlined templates}
- to get it working on jsfiddle as well.
-
-
-
-
- Choose:
-
Moby |
-
Moby: Ch1 |
-
Gatsby |
-
Gatsby: Ch4 |
-
Scarlet Letter
-
-
-
-
-
$location.path() = {{$location.path()}}
-
$route.current.templateUrl = {{$route.current.templateUrl}}
-
$route.current.params = {{$route.current.params}}
-
$route.current.scope.name = {{$route.current.scope.name}}
-
$routeParams = {{$routeParams}}
-
-
-
-
- controller: {{name}}
- Book Id: {{params.bookId}}
-
-
-
- controller: {{name}}
- Book Id: {{params.bookId}}
- Chapter Id: {{params.chapterId}}
-
-
-
- angular.module('ngRouteExample', ['ngRoute'])
-
- .config(function($routeProvider, $locationProvider) {
- $routeProvider.when('/Book/:bookId', {
- templateUrl: 'book.html',
- controller: BookCntl,
- resolve: {
- // I will cause a 1 second delay
- delay: function($q, $timeout) {
- var delay = $q.defer();
- $timeout(delay.resolve, 1000);
- return delay.promise;
- }
- }
- });
- $routeProvider.when('/Book/:bookId/ch/:chapterId', {
- templateUrl: 'chapter.html',
- controller: ChapterCntl
- });
-
- // configure html5 to get links working on jsfiddle
- $locationProvider.html5Mode(true);
- });
-
- function MainCntl($scope, $route, $routeParams, $location) {
- $scope.$route = $route;
- $scope.$location = $location;
- $scope.$routeParams = $routeParams;
- }
-
- function BookCntl($scope, $routeParams) {
- $scope.name = "BookCntl";
- $scope.params = $routeParams;
- }
-
- function ChapterCntl($scope, $routeParams) {
- $scope.name = "ChapterCntl";
- $scope.params = $routeParams;
- }
-
-
-
- it('should load and compile correct template', function() {
- element(by.linkText('Moby: Ch1')).click();
- var content = element(by.css('[ng-view]')).getText();
- expect(content).toMatch(/controller\: ChapterCntl/);
- expect(content).toMatch(/Book Id\: Moby/);
- expect(content).toMatch(/Chapter Id\: 1/);
-
- element(by.partialLinkText('Scarlet')).click();
-
- content = element(by.css('[ng-view]')).getText();
- expect(content).toMatch(/controller\: BookCntl/);
- expect(content).toMatch(/Book Id\: Scarlet/);
- });
-
-
+ * This example shows how changing the URL hash causes the `$route` to match a route against the
+ * URL, and the `ngView` pulls in the partial.
+ *
+ * Note that this example is using {@link ng.directive:script inlined templates}
+ * to get it working on jsfiddle as well.
+ *
+ *
+ *
+ *
+ * Choose:
+ *
Moby |
+ *
Moby: Ch1 |
+ *
Gatsby |
+ *
Gatsby: Ch4 |
+ *
Scarlet Letter
+ *
+ *
+ *
+ *
+ *
+ *
$location.path() = {{$location.path()}}
+ *
$route.current.templateUrl = {{$route.current.templateUrl}}
+ *
$route.current.params = {{$route.current.params}}
+ *
$route.current.scope.name = {{$route.current.scope.name}}
+ *
$routeParams = {{$routeParams}}
+ *
+ *
+ *
+ *
+ * controller: {{name}}
+ * Book Id: {{params.bookId}}
+ *
+ *
+ *
+ * controller: {{name}}
+ * Book Id: {{params.bookId}}
+ * Chapter Id: {{params.chapterId}}
+ *
+ *
+ *
+ * angular.module('ngRouteExample', ['ngRoute'])
+ *
+ * .controller('MainController', function($scope, $route, $routeParams, $location) {
+ * $scope.$route = $route;
+ * $scope.$location = $location;
+ * $scope.$routeParams = $routeParams;
+ * })
+ *
+ * .controller('BookController', function($scope, $routeParams) {
+ * $scope.name = "BookController";
+ * $scope.params = $routeParams;
+ * })
+ *
+ * .controller('ChapterController', function($scope, $routeParams) {
+ * $scope.name = "ChapterController";
+ * $scope.params = $routeParams;
+ * })
+ *
+ * .config(function($routeProvider, $locationProvider) {
+ * $routeProvider
+ * .when('/Book/:bookId', {
+ * templateUrl: 'book.html',
+ * controller: 'BookController',
+ * resolve: {
+ * // I will cause a 1 second delay
+ * delay: function($q, $timeout) {
+ * var delay = $q.defer();
+ * $timeout(delay.resolve, 1000);
+ * return delay.promise;
+ * }
+ * }
+ * })
+ * .when('/Book/:bookId/ch/:chapterId', {
+ * templateUrl: 'chapter.html',
+ * controller: 'ChapterController'
+ * });
+ *
+ * // configure html5 to get links working on jsfiddle
+ * $locationProvider.html5Mode(true);
+ * });
+ *
+ *
+ *
+ *
+ * it('should load and compile correct template', function() {
+ * element(by.linkText('Moby: Ch1')).click();
+ * var content = element(by.css('[ng-view]')).getText();
+ * expect(content).toMatch(/controller\: ChapterController/);
+ * expect(content).toMatch(/Book Id\: Moby/);
+ * expect(content).toMatch(/Chapter Id\: 1/);
+ *
+ * element(by.partialLinkText('Scarlet')).click();
+ *
+ * content = element(by.css('[ng-view]')).getText();
+ * expect(content).toMatch(/controller\: BookController/);
+ * expect(content).toMatch(/Book Id\: Scarlet/);
+ * });
+ *
+ *
*/
/**
diff --git a/src/ngSanitize/sanitize.js b/src/ngSanitize/sanitize.js
index 35dc8f100bec..38d088bbe407 100644
--- a/src/ngSanitize/sanitize.js
+++ b/src/ngSanitize/sanitize.js
@@ -254,7 +254,7 @@ function htmlParser( html, handler ) {
match = html.match( DOCTYPE_REGEXP );
if ( match ) {
- html = html.replace( match[0] , '');
+ html = html.replace( match[0], '');
chars = false;
}
// end tag
diff --git a/src/ngTouch/directive/ngClick.js b/src/ngTouch/directive/ngClick.js
index 6cd0ff763032..7e558defc9f5 100644
--- a/src/ngTouch/directive/ngClick.js
+++ b/src/ngTouch/directive/ngClick.js
@@ -53,6 +53,7 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
var ACTIVE_CLASS_NAME = 'ng-click-active';
var lastPreventedTime;
var touchCoordinates;
+ var lastLabelClickCoordinates;
// TAP EVENTS AND GHOST CLICKS
@@ -124,10 +125,23 @@ ngTouch.directive('ngClick', ['$parse', '$timeout', '$rootElement',
var y = touches[0].clientY;
// Work around desktop Webkit quirk where clicking a label will fire two clicks (on the label
// and on the input element). Depending on the exact browser, this second click we don't want
- // to bust has either (0,0) or negative coordinates.
+ // to bust has either (0,0), negative coordinates, or coordinates equal to triggering label
+ // click event
if (x < 1 && y < 1) {
return; // offscreen
}
+ if (lastLabelClickCoordinates &&
+ lastLabelClickCoordinates[0] === x && lastLabelClickCoordinates[1] === y) {
+ return; // input click triggered by label click
+ }
+ // reset label click coordinates on first subsequent click
+ if (lastLabelClickCoordinates) {
+ lastLabelClickCoordinates = null;
+ }
+ // remember label click coordinates to prevent click busting of trigger click event on input
+ if (event.target.tagName.toLowerCase() === 'label') {
+ lastLabelClickCoordinates = [x, y];
+ }
// Look for an allowable region containing this click.
// If we find one, that means it was created by touchstart and not removed by
diff --git a/test/auto/injectorSpec.js b/test/auto/injectorSpec.js
index a70cd763793e..4b9679784c0e 100644
--- a/test/auto/injectorSpec.js
+++ b/test/auto/injectorSpec.js
@@ -163,7 +163,7 @@ describe('injector', function() {
function $f_n0 /*
*/(
$a, // x, <-- looks like an arg but it is a comment
- b_ , /* z, <-- looks like an arg but it is a
+ b_, /* z, <-- looks like an arg but it is a
multi-line comment
function (a, b) {}
*/
diff --git a/test/helpers/testabilityPatch.js b/test/helpers/testabilityPatch.js
index f61be4eeb13f..34b6b78a0141 100644
--- a/test/helpers/testabilityPatch.js
+++ b/test/helpers/testabilityPatch.js
@@ -269,21 +269,27 @@ function provideLog($provide) {
log.toString = function() {
return messages.join('; ');
- }
+ };
log.toArray = function() {
return messages;
- }
+ };
log.reset = function() {
messages = [];
+ };
+
+ log.empty = function() {
+ var currentMessages = messages;
+ messages = [];
+ return currentMessages;
}
log.fn = function(msg) {
return function() {
log(msg);
- }
- }
+ };
+ };
log.$$log = true;
diff --git a/test/jqLiteSpec.js b/test/jqLiteSpec.js
index 482c05f4f730..faf1c98cbb8c 100644
--- a/test/jqLiteSpec.js
+++ b/test/jqLiteSpec.js
@@ -168,6 +168,19 @@ describe('jqLite', function() {
dealoc(ul);
});
+
+ it('should pass through DocumentFragment boundaries via host', function() {
+ var host = jqLite('
'),
+ frag = document.createDocumentFragment(),
+ $frag = jqLite(frag);
+ frag.host = host[0];
+ host.data("foo", 123);
+ host.append($frag);
+ expect($frag.inheritedData("foo")).toBe(123);
+
+ dealoc(host);
+ dealoc($frag);
+ });
});
diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js
index 98b1650f7706..5110c4d62634 100755
--- a/test/ng/compileSpec.js
+++ b/test/ng/compileSpec.js
@@ -529,10 +529,18 @@ describe('$compile', function() {
replace: true,
template: 'TH '
}));
+ directive('replaceWithThead', valueFn({
+ replace: true,
+ template: 'TD '
+ }));
directive('replaceWithTbody', valueFn({
replace: true,
template: 'TD '
}));
+ directive('replaceWithTfoot', valueFn({
+ replace: true,
+ template: 'TD '
+ }));
}));
@@ -718,12 +726,26 @@ describe('$compile', function() {
expect(nodeName_(element)).toMatch(/th/i);
}));
+ it('should support templates with root tags', inject(function($compile, $rootScope) {
+ expect(function() {
+ element = $compile('
')($rootScope);
+ }).not.toThrow();
+ expect(nodeName_(element)).toMatch(/thead/i);
+ }));
+
it('should support templates with root tags', inject(function($compile, $rootScope) {
expect(function() {
element = $compile('
')($rootScope);
}).not.toThrow();
expect(nodeName_(element)).toMatch(/tbody/i);
}));
+
+ it('should support templates with root tags', inject(function($compile, $rootScope) {
+ expect(function() {
+ element = $compile('
')($rootScope);
+ }).not.toThrow();
+ expect(nodeName_(element)).toMatch(/tfoot/i);
+ }));
});
@@ -833,10 +855,18 @@ describe('$compile', function() {
replace: true,
templateUrl: 'th.html'
}));
+ directive('replaceWithThead', valueFn({
+ replace: true,
+ templateUrl: 'thead.html'
+ }));
directive('replaceWithTbody', valueFn({
replace: true,
templateUrl: 'tbody.html'
}));
+ directive('replaceWithTfoot', valueFn({
+ replace: true,
+ templateUrl: 'tfoot.html'
+ }));
}
));
@@ -1500,6 +1530,15 @@ describe('$compile', function() {
expect(nodeName_(element)).toMatch(/th/i);
}));
+ it('should support templates with root tags', inject(function($compile, $rootScope, $templateCache) {
+ $templateCache.put('thead.html', 'TD ');
+ expect(function() {
+ element = $compile('
')($rootScope);
+ }).not.toThrow();
+ $rootScope.$digest();
+ expect(nodeName_(element)).toMatch(/thead/i);
+ }));
+
it('should support templates with root tags', inject(function($compile, $rootScope, $templateCache) {
$templateCache.put('tbody.html', ' TD ');
expect(function() {
@@ -1508,6 +1547,15 @@ describe('$compile', function() {
$rootScope.$digest();
expect(nodeName_(element)).toMatch(/tbody/i);
}));
+
+ it('should support templates with root tags', inject(function($compile, $rootScope, $templateCache) {
+ $templateCache.put('tfoot.html', 'TD ');
+ expect(function() {
+ element = $compile('
')($rootScope);
+ }).not.toThrow();
+ $rootScope.$digest();
+ expect(nodeName_(element)).toMatch(/tfoot/i);
+ }));
});
diff --git a/test/ng/directive/selectSpec.js b/test/ng/directive/selectSpec.js
index 6fcd1fe05f82..d270f438704f 100644
--- a/test/ng/directive/selectSpec.js
+++ b/test/ng/directive/selectSpec.js
@@ -733,6 +733,27 @@ describe('select', function() {
expect(sortedHtml(options[2])).toEqual('3 ');
});
+ it('should not update selected property of an option element on digest with no change event',
+ function() {
+ createSingleSelect();
+
+ scope.$apply(function() {
+ scope.values = [{name: 'A'}, {name: 'B'}, {name: 'C'}];
+ scope.selected = scope.values[0];
+ });
+
+ var options = element.find('option');
+ var optionToSelect = options.eq(1);
+
+ expect(optionToSelect.text()).toBe('B');
+
+ optionToSelect.prop('selected', true);
+ scope.$digest();
+
+ expect(optionToSelect.prop('selected')).toBe(true);
+ expect(scope.selected).toBe(scope.values[0]);
+ });
+
describe('binding', function() {
it('should bind to scope value', function() {
diff --git a/test/ng/filter/orderBySpec.js b/test/ng/filter/orderBySpec.js
index 5c1178910255..5dc966777419 100644
--- a/test/ng/filter/orderBySpec.js
+++ b/test/ng/filter/orderBySpec.js
@@ -31,4 +31,16 @@ describe('Filter: orderBy', function() {
toEqual([{a:2, b:1},{a:15, b:1}]);
});
+ it('should support string predicates with names containing non-identifier characters', function() {
+ expect(orderBy([{"Tip %": .25}, {"Tip %": .15}, {"Tip %": .40}], '"Tip %"'))
+ .toEqualData([{"Tip %": .15}, {"Tip %": .25}, {"Tip %": .40}]);
+ expect(orderBy([{"원": 76000}, {"원": 31000}, {"원": 156000}], '"원"'))
+ .toEqualData([{"원": 31000}, {"원": 76000}, {"원": 156000}])
+ });
+
+ it('should throw if quoted string predicate is quoted incorrectly', function() {
+ expect(function() {
+ return orderBy([{"Tip %": .15}, {"Tip %": .25}, {"Tip %": .40}], '"Tip %\'');
+ }).toThrow();
+ });
});
diff --git a/test/ng/httpBackendSpec.js b/test/ng/httpBackendSpec.js
index 2a3f60126737..5c9cbf586c36 100644
--- a/test/ng/httpBackendSpec.js
+++ b/test/ng/httpBackendSpec.js
@@ -456,27 +456,45 @@ describe('$httpBackend', function() {
}
- it('should convert 0 to 200 if content', function() {
+ it('should convert 0 to 200 if content and file protocol', function() {
$backend = createHttpBackend($browser, createMockXhr);
- $backend('GET', 'someProtocol:///whatever/index.html', null, callback);
+ $backend('GET', 'file:///whatever/index.html', null, callback);
respond(0, 'SOME CONTENT');
expect(callback).toHaveBeenCalled();
expect(callback.mostRecentCall.args[0]).toBe(200);
});
-
- it('should convert 0 to 404 if no content', function() {
+ it('should convert 0 to 200 if content for protocols other than file', function() {
$backend = createHttpBackend($browser, createMockXhr);
$backend('GET', 'someProtocol:///whatever/index.html', null, callback);
+ respond(0, 'SOME CONTENT');
+
+ expect(callback).toHaveBeenCalled();
+ expect(callback.mostRecentCall.args[0]).toBe(200);
+ });
+
+ it('should convert 0 to 404 if no content and file protocol', function() {
+ $backend = createHttpBackend($browser, createMockXhr);
+
+ $backend('GET', 'file:///whatever/index.html', null, callback);
respond(0, '');
expect(callback).toHaveBeenCalled();
expect(callback.mostRecentCall.args[0]).toBe(404);
});
+ it('should not convert 0 to 404 if no content for protocols other than file', function() {
+ $backend = createHttpBackend($browser, createMockXhr);
+
+ $backend('GET', 'someProtocol:///whatever/index.html', null, callback);
+ respond(0, '');
+
+ expect(callback).toHaveBeenCalled();
+ expect(callback.mostRecentCall.args[0]).toBe(0);
+ });
it('should convert 0 to 404 if no content - relative url', function() {
var originalUrlParsingNode = urlParsingNode;
@@ -486,10 +504,10 @@ describe('$httpBackend', function() {
hash : "#/C:/",
host : "",
hostname : "",
- href : "someProtocol:///C:/base#!/C:/foo",
+ href : "file:///C:/base#!/C:/foo",
pathname : "/C:/foo",
port : "",
- protocol : "someProtocol:",
+ protocol : "file:",
search : "",
setAttribute: angular.noop
};
diff --git a/test/ng/httpSpec.js b/test/ng/httpSpec.js
index a7b244831f6f..86ab72ea8bd8 100644
--- a/test/ng/httpSpec.js
+++ b/test/ng/httpSpec.js
@@ -989,6 +989,16 @@ describe('$http', function() {
});
+ it('should ignore Blob objects', function () {
+ if (!window.Blob) return;
+
+ var blob = new Blob(['blob!'], { type: 'text/plain' });
+
+ $httpBackend.expect('POST', '/url', '[object Blob]').respond('');
+ $http({ method: 'POST', url: '/url', data: blob });
+ });
+
+
it('should have access to request headers', function() {
$httpBackend.expect('POST', '/url', 'header1').respond(200);
$http.post('/url', 'req', {
diff --git a/test/ng/rafSpec.js b/test/ng/rafSpec.js
index 6c15e2d2984c..7c67b8c9409f 100644
--- a/test/ng/rafSpec.js
+++ b/test/ng/rafSpec.js
@@ -31,6 +31,35 @@ describe('$$rAF', function() {
expect(present).toBe(true);
}));
+ describe('$timeout fallback', function() {
+ it("it should use a $timeout incase native rAF isn't suppored", function() {
+ var timeoutSpy = jasmine.createSpy('callback');
+
+ //we need to create our own injector to work around the ngMock overrides
+ var injector = createInjector(['ng', function($provide) {
+ $provide.value('$timeout', timeoutSpy);
+ $provide.value('$window', {
+ location : window.location,
+ });
+ }]);
+
+ var $$rAF = injector.get('$$rAF');
+ expect($$rAF.supported).toBe(false);
+
+ var message;
+ $$rAF(function() {
+ message = 'on';
+ });
+
+ expect(message).toBeUndefined();
+ expect(timeoutSpy).toHaveBeenCalled();
+
+ timeoutSpy.mostRecentCall.args[0]();
+
+ expect(message).toBe('on');
+ });
+ });
+
describe('mocks', function() {
it('should throw an error if no frames are present', inject(function($$rAF) {
if($$rAF.supported) {
@@ -44,4 +73,29 @@ describe('$$rAF', function() {
}
}));
});
+
+ describe('mobile', function() {
+ it('should provide a cancellation method for an older version of Android', function() {
+ //we need to create our own injector to work around the ngMock overrides
+ var injector = createInjector(['ng', function($provide) {
+ $provide.value('$window', {
+ location : window.location,
+ webkitRequestAnimationFrame: jasmine.createSpy('$window.webkitRequestAnimationFrame'),
+ webkitCancelRequestAnimationFrame: jasmine.createSpy('$window.webkitCancelRequestAnimationFrame')
+ });
+ }]);
+
+ var $$rAF = injector.get('$$rAF');
+ var $window = injector.get('$window');
+ var cancel = $$rAF(function() {});
+
+ expect($$rAF.supported).toBe(true);
+
+ try {
+ cancel();
+ } catch(e) {}
+
+ expect($window.webkitCancelRequestAnimationFrame).toHaveBeenCalled();
+ });
+ });
});
diff --git a/test/ng/rootScopeSpec.js b/test/ng/rootScopeSpec.js
index f9cf9412c605..86436ea81f81 100644
--- a/test/ng/rootScopeSpec.js
+++ b/test/ng/rootScopeSpec.js
@@ -483,102 +483,131 @@ describe('Scope', function() {
describe('$watchCollection', function() {
var log, $rootScope, deregister;
- beforeEach(inject(function(_$rootScope_) {
- log = [];
+ beforeEach(inject(function(_$rootScope_, _log_) {
$rootScope = _$rootScope_;
- deregister = $rootScope.$watchCollection('obj', function logger(obj) {
- log.push(toJson(obj));
+ log = _log_;
+ deregister = $rootScope.$watchCollection('obj', function logger(newVal, oldVal) {
+ var msg = {newVal: newVal, oldVal: oldVal};
+
+ if (newVal === oldVal) {
+ msg.identical = true;
+ }
+
+ log(msg);
});
}));
it('should not trigger if nothing change', inject(function($rootScope) {
$rootScope.$digest();
- expect(log).toEqual([undefined]);
+ expect(log).toEqual([{ newVal : undefined, oldVal : undefined, identical : true }]);
+ log.reset();
$rootScope.$digest();
- expect(log).toEqual([undefined]);
+ expect(log).toEqual([]);
}));
- it('should allow deregistration', inject(function($rootScope) {
+ it('should allow deregistration', function() {
$rootScope.obj = [];
$rootScope.$digest();
-
- expect(log).toEqual(['[]']);
+ expect(log.toArray().length).toBe(1);
+ log.reset();
$rootScope.obj.push('a');
deregister();
$rootScope.$digest();
- expect(log).toEqual(['[]']);
- }));
+ expect(log).toEqual([]);
+ });
describe('array', function() {
+
+ it('should return oldCollection === newCollection only on the first listener call',
+ inject(function($rootScope, log) {
+
+ // first time should be identical
+ $rootScope.obj = ['a', 'b'];
+ $rootScope.$digest();
+ expect(log).toEqual([{newVal: ['a', 'b'], oldVal: ['a', 'b'], identical: true}]);
+ log.reset();
+
+ // second time should be different
+ $rootScope.obj[1] = 'c';
+ $rootScope.$digest();
+ expect(log).toEqual([{newVal: ['a', 'c'], oldVal: ['a', 'b']}]);
+ }));
+
+
it('should trigger when property changes into array', function() {
$rootScope.obj = 'test';
$rootScope.$digest();
- expect(log).toEqual(['"test"']);
+ expect(log.empty()).toEqual([{newVal: "test", oldVal: "test", identical: true}]);
$rootScope.obj = [];
$rootScope.$digest();
- expect(log).toEqual(['"test"', '[]']);
+ expect(log.empty()).toEqual([{newVal: [], oldVal: "test"}]);
$rootScope.obj = {};
$rootScope.$digest();
- expect(log).toEqual(['"test"', '[]', '{}']);
+ expect(log.empty()).toEqual([{newVal: {}, oldVal: []}]);
$rootScope.obj = [];
$rootScope.$digest();
- expect(log).toEqual(['"test"', '[]', '{}', '[]']);
+ expect(log.empty()).toEqual([{newVal: [], oldVal: {}}]);
$rootScope.obj = undefined;
$rootScope.$digest();
- expect(log).toEqual(['"test"', '[]', '{}', '[]', undefined]);
+ expect(log.empty()).toEqual([{newVal: undefined, oldVal: []}]);
});
it('should not trigger change when object in collection changes', function() {
$rootScope.obj = [{}];
$rootScope.$digest();
- expect(log).toEqual(['[{}]']);
+ expect(log.empty()).toEqual([{newVal: [{}], oldVal: [{}], identical: true}]);
$rootScope.obj[0].name = 'foo';
$rootScope.$digest();
- expect(log).toEqual(['[{}]']);
+ expect(log).toEqual([]);
});
it('should watch array properties', function() {
$rootScope.obj = [];
$rootScope.$digest();
- expect(log).toEqual(['[]']);
+ expect(log.empty()).toEqual([{newVal: [], oldVal: [], identical: true}]);
$rootScope.obj.push('a');
$rootScope.$digest();
- expect(log).toEqual(['[]', '["a"]']);
+ expect(log.empty()).toEqual([{newVal: ['a'], oldVal: []}]);
$rootScope.obj[0] = 'b';
$rootScope.$digest();
- expect(log).toEqual(['[]', '["a"]', '["b"]']);
+ expect(log.empty()).toEqual([{newVal: ['b'], oldVal: ['a']}]);
$rootScope.obj.push([]);
$rootScope.obj.push({});
- log = [];
$rootScope.$digest();
- expect(log).toEqual(['["b",[],{}]']);
+ expect(log.empty()).toEqual([{newVal: ['b', [], {}], oldVal: ['b']}]);
var temp = $rootScope.obj[1];
$rootScope.obj[1] = $rootScope.obj[2];
$rootScope.obj[2] = temp;
$rootScope.$digest();
- expect(log).toEqual([ '["b",[],{}]', '["b",{},[]]' ]);
+ expect(log.empty()).toEqual([{newVal: ['b', {}, []], oldVal: ['b', [], {}]}]);
$rootScope.obj.shift();
- log = [];
$rootScope.$digest();
- expect(log).toEqual([ '[{},[]]' ]);
+ expect(log.empty()).toEqual([{newVal: [{}, []], oldVal: ['b', {}, []]}]);
+ });
+
+ it('should not infinitely digest when current value is NaN', function() {
+ $rootScope.obj = [NaN];
+ expect(function() {
+ $rootScope.$digest();
+ }).not.toThrow();
});
it('should watch array-like objects like arrays', function () {
@@ -601,57 +630,72 @@ describe('Scope', function() {
describe('object', function() {
+
+ it('should return oldCollection === newCollection only on the first listener call',
+ inject(function($rootScope, log) {
+
+ $rootScope.obj = {'a': 'b'};
+ // first time should be identical
+ $rootScope.$digest();
+ expect(log.empty()).toEqual([{newVal: {'a': 'b'}, oldVal: {'a': 'b'}, identical: true}]);
+
+ // second time not identical
+ $rootScope.obj.a = 'c';
+ $rootScope.$digest();
+ expect(log).toEqual([{newVal: {'a': 'c'}, oldVal: {'a': 'b'}}]);
+ }));
+
+
it('should trigger when property changes into object', function() {
$rootScope.obj = 'test';
$rootScope.$digest();
- expect(log).toEqual(['"test"']);
+ expect(log.empty()).toEqual([{newVal: 'test', oldVal: 'test', identical: true}]);
$rootScope.obj = {};
$rootScope.$digest();
- expect(log).toEqual(['"test"', '{}']);
+ expect(log.empty()).toEqual([{newVal: {}, oldVal: 'test'}]);
});
it('should not trigger change when object in collection changes', function() {
$rootScope.obj = {name: {}};
$rootScope.$digest();
- expect(log).toEqual(['{"name":{}}']);
+ expect(log.empty()).toEqual([{newVal: {name: {}}, oldVal: {name: {}}, identical: true}]);
$rootScope.obj.name.bar = 'foo';
$rootScope.$digest();
- expect(log).toEqual(['{"name":{}}']);
+ expect(log.empty()).toEqual([]);
});
it('should watch object properties', function() {
$rootScope.obj = {};
$rootScope.$digest();
- expect(log).toEqual(['{}']);
+ expect(log.empty()).toEqual([{newVal: {}, oldVal: {}, identical: true}]);
$rootScope.obj.a= 'A';
$rootScope.$digest();
- expect(log).toEqual(['{}', '{"a":"A"}']);
+ expect(log.empty()).toEqual([{newVal: {a: 'A'}, oldVal: {}}]);
$rootScope.obj.a = 'B';
$rootScope.$digest();
- expect(log).toEqual(['{}', '{"a":"A"}', '{"a":"B"}']);
+ expect(log.empty()).toEqual([{newVal: {a: 'B'}, oldVal: {a: 'A'}}]);
$rootScope.obj.b = [];
$rootScope.obj.c = {};
- log = [];
$rootScope.$digest();
- expect(log).toEqual(['{"a":"B","b":[],"c":{}}']);
+ expect(log.empty()).toEqual([{newVal: {a: 'B', b: [], c: {}}, oldVal: {a: 'B'}}]);
var temp = $rootScope.obj.a;
$rootScope.obj.a = $rootScope.obj.b;
$rootScope.obj.c = temp;
$rootScope.$digest();
- expect(log).toEqual([ '{"a":"B","b":[],"c":{}}', '{"a":[],"b":[],"c":"B"}' ]);
+ expect(log.empty()).
+ toEqual([{newVal: {a: [], b: {}, c: 'B'}, oldVal: {a: 'B', b: [], c: {}}}]);
delete $rootScope.obj.a;
- log = [];
$rootScope.$digest();
- expect(log).toEqual([ '{"b":[],"c":"B"}' ]);
+ expect(log.empty()).toEqual([{newVal: {b: {}, c: 'B'}, oldVal: {a: [], b: {}, c: 'B'}}]);
});
});
});
diff --git a/test/ng/sceSpecs.js b/test/ng/sceSpecs.js
index e2a16c1cbc0f..40e79c5737ff 100644
--- a/test/ng/sceSpecs.js
+++ b/test/ng/sceSpecs.js
@@ -109,7 +109,7 @@ describe('SCE', function() {
}));
it('should NOT wrap unknown contexts', inject(function($sce) {
- expect(function() { $sce.trustAs('unknown1' , '123'); }).toThrowMinErr(
+ expect(function() { $sce.trustAs('unknown1', '123'); }).toThrowMinErr(
'$sce', 'icontext', 'Attempted to trust a value in invalid context. Context: unknown1; Value: 123');
}));
diff --git a/test/ng/snifferSpec.js b/test/ng/snifferSpec.js
index 3f8d33510273..db44b2f1378f 100644
--- a/test/ng/snifferSpec.js
+++ b/test/ng/snifferSpec.js
@@ -332,6 +332,29 @@ describe('$sniffer', function() {
expect($sniffer.history).toBe(false);
});
});
+
+ it('should be false on Webkit versions older then 534.x.x', function() {
+ module(function($provide) {
+ var doc = {
+ body : {
+ style : {}
+ }
+ };
+ var win = {
+ history: {
+ pushState: noop
+ },
+ navigator: {
+ userAgent: 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_8; nl-nl) AppleWebkit/533.18.1 (KHTML, like Gecko) Version/5.0.2 Safari/533.18.5'
+ }
+ };
+ $provide.value('$document', jqLite(doc));
+ $provide.value('$window', win);
+ });
+ inject(function($sniffer) {
+ expect($sniffer.history).toBe(false);
+ });
+ });
});
it('should provide the android version', function() {
diff --git a/test/ngAnimate/animateSpec.js b/test/ngAnimate/animateSpec.js
index fb9ba19e8f19..204ca9c32114 100644
--- a/test/ngAnimate/animateSpec.js
+++ b/test/ngAnimate/animateSpec.js
@@ -3356,6 +3356,49 @@ describe("ngAnimate", function() {
});
});
+ it('should animate only the specified CSS className inside ng-if', function() {
+ var captures = {};
+ module(function($animateProvider) {
+ $animateProvider.classNameFilter(/prefixed-animation/);
+ $animateProvider.register('.capture', function() {
+ return {
+ enter : buildFn('enter'),
+ leave : buildFn('leave')
+ };
+
+ function buildFn(key) {
+ return function(element, className, done) {
+ captures[key] = true;
+ (done || className)();
+ }
+ }
+ });
+ });
+ inject(function($rootScope, $compile, $rootElement, $document, $sniffer, $animate) {
+ if(!$sniffer.transitions) return;
+
+ var upperElement = $compile('')($rootScope);
+ $rootElement.append(upperElement);
+ jqLite($document[0].body).append($rootElement);
+
+ $rootScope.$digest();
+ $animate.triggerCallbacks();
+
+ var element = upperElement.find('span');
+
+ var leaveDone = false;
+ $animate.leave(element, function() {
+ leaveDone = true;
+ });
+
+ $rootScope.$digest();
+ $animate.triggerCallbacks();
+
+ expect(captures['leave']).toBe(true);
+ expect(leaveDone).toBe(true);
+ });
+ });
+
it('should respect the most relevant CSS transition property if defined in multiple classes',
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
diff --git a/test/ngCookies/cookiesSpec.js b/test/ngCookies/cookiesSpec.js
index 674c27748f11..1d669c1c6b8a 100644
--- a/test/ngCookies/cookiesSpec.js
+++ b/test/ngCookies/cookiesSpec.js
@@ -45,15 +45,25 @@ describe('$cookies', function() {
}));
- it('should drop or reset any cookie that was set to a non-string value',
+ it('should convert non-string values to string',
inject(function($cookies, $browser, $rootScope) {
$cookies.nonString = [1, 2, 3];
$cookies.nullVal = null;
$cookies.undefVal = undefined;
- $cookies.preexisting = function() {};
+ var preexisting = $cookies.preexisting = function() {};
$rootScope.$digest();
- expect($browser.cookies()).toEqual({'preexisting': 'oldCookie'});
- expect($cookies).toEqual({'preexisting': 'oldCookie'});
+ expect($browser.cookies()).toEqual({
+ 'preexisting': '' + preexisting,
+ 'nonString': '1,2,3',
+ 'nullVal': 'null',
+ 'undefVal': 'undefined'
+ });
+ expect($cookies).toEqual({
+ 'preexisting': '' + preexisting,
+ 'nonString': '1,2,3',
+ 'nullVal': 'null',
+ 'undefVal': 'undefined'
+ });
}));
diff --git a/test/ngMock/angular-mocksSpec.js b/test/ngMock/angular-mocksSpec.js
index c2b6108dfb33..b78e9bbf207a 100644
--- a/test/ngMock/angular-mocksSpec.js
+++ b/test/ngMock/angular-mocksSpec.js
@@ -52,16 +52,19 @@ describe('ngMock', function() {
it('should fake getHours method', function() {
- //0 in -3h
- var t0 = new angular.mock.TzDate(-3, 0);
+ // avoid going negative due to #5017, so use Jan 2, 1970 00:00 UTC
+ var jan2 = 24 * 60 * 60 * 1000;
+
+ //0:00 in -3h
+ var t0 = new angular.mock.TzDate(-3, jan2);
expect(t0.getHours()).toBe(3);
- //0 in +0h
- var t1 = new angular.mock.TzDate(0, 0);
+ //0:00 in +0h
+ var t1 = new angular.mock.TzDate(0, jan2);
expect(t1.getHours()).toBe(0);
- //0 in +3h
- var t2 = new angular.mock.TzDate(3, 0);
+ //0:00 in +3h
+ var t2 = new angular.mock.TzDate(3, jan2);
expect(t2.getHours()).toMatch(21);
});
diff --git a/test/ngTouch/directive/ngClickSpec.js b/test/ngTouch/directive/ngClickSpec.js
index 43735709abea..921c64578b2b 100644
--- a/test/ngTouch/directive/ngClickSpec.js
+++ b/test/ngTouch/directive/ngClickSpec.js
@@ -370,40 +370,100 @@ describe('ngClick (touch)', function() {
}));
- it('should not cancel clicks that come long after', inject(function($rootScope, $compile) {
- element1 = $compile('
')($rootScope);
+ describe('when clicking on a label immediately following a touch event', function() {
+ var touch = function(element, x, y) {
+ time = 10;
+ browserTrigger(element, 'touchstart',{
+ keys: [],
+ x: x,
+ y: y
+ });
- $rootScope.count = 0;
+ time = 50;
+ browserTrigger(element, 'touchend',{
+ keys: [],
+ x: x,
+ y: y
+ });
+ };
- $rootScope.$digest();
+ var click = function(element, x, y) {
+ browserTrigger(element, 'click',{
+ keys: [],
+ x: x,
+ y: y
+ });
+ };
- expect($rootScope.count).toBe(0);
+ var $rootScope;
+ var container, otherElement, input, label;
+ beforeEach(inject(function(_$rootScope_, $compile, $rootElement) {
+ $rootScope = _$rootScope_;
+ var container = $compile('')($rootScope);
+ $rootElement.append(container);
+ otherElement = container.children()[0];
+ input = container.children()[1];
+ label = container.children()[2];
- time = 10;
- browserTrigger(element1, 'touchstart',{
- keys: [],
- x: 10,
- y: 10
+ $rootScope.selection = 'initial';
+
+ $rootScope.$digest();
+ }));
+
+
+ afterEach(function() {
+ dealoc(label);
+ dealoc(input);
+ dealoc(otherElement);
+ dealoc(container);
});
- time = 50;
- browserTrigger(element1, 'touchend',{
- keys: [],
- x: 10,
- y: 10
+
+ it('should not cancel input clicks with (0,0) coordinates', function() {
+ touch(otherElement, 100, 100);
+
+ time = 500;
+ click(label, 10, 10);
+ click(input, 0, 0);
+
+ expect($rootScope.selection).toBe('radio1');
});
- expect($rootScope.count).toBe(1);
- time = 2700;
- browserTrigger(element1, 'click',{
- keys: [],
- x: 10,
- y: 10
+ it('should not cancel input clicks with negative coordinates', function() {
+ touch(otherElement, 100, 100);
+
+ time = 500;
+ click(label, 10, 10);
+ click(input, -1, -1);
+
+ expect($rootScope.selection).toBe('radio1');
});
- expect($rootScope.count).toBe(2);
- }));
+
+ it('should not cancel input clicks with positive coordinates identical to label click', function() {
+ touch(otherElement, 100, 100);
+
+ time = 500;
+ click(label, 10, 10);
+ click(input, 10, 10);
+
+ expect($rootScope.selection).toBe('radio1');
+ });
+
+
+ it('should cancel input clicks with positive coordinates different than label click', function() {
+ touch(otherElement, 100, 100);
+
+ time = 500;
+ click(label, 10, 10);
+ click(input, 11, 11);
+
+ expect($rootScope.selection).toBe('initial');
+ });
+ });
});
diff --git a/test/ngTouch/directive/ngSwipeSpec.js b/test/ngTouch/directive/ngSwipeSpec.js
index 2aa1a8fe338d..b46a9384e5f9 100644
--- a/test/ngTouch/directive/ngSwipeSpec.js
+++ b/test/ngTouch/directive/ngSwipeSpec.js
@@ -222,6 +222,6 @@ var swipeTests = function(description, restrictBrowsers, startEvent, moveEvent,
});
}
-swipeTests('touch', true /* restrictBrowers */, 'touchstart', 'touchmove', 'touchend');
-swipeTests('mouse', false /* restrictBrowers */, 'mousedown', 'mousemove', 'mouseup');
+swipeTests('touch', /* restrictBrowers */ true, 'touchstart', 'touchmove', 'touchend');
+swipeTests('mouse', /* restrictBrowers */ false, 'mousedown', 'mousemove', 'mouseup');
diff --git a/test/ngTouch/swipeSpec.js b/test/ngTouch/swipeSpec.js
index 435dda8c0109..1eb53e45d619 100644
--- a/test/ngTouch/swipeSpec.js
+++ b/test/ngTouch/swipeSpec.js
@@ -387,6 +387,6 @@ var swipeTests = function(description, restrictBrowsers, startEvent, moveEvent,
});
}
-swipeTests('touch', true /* restrictBrowers */, 'touchstart', 'touchmove', 'touchend');
-swipeTests('mouse', false /* restrictBrowers */, 'mousedown', 'mousemove', 'mouseup');
+swipeTests('touch', /* restrictBrowers */ true, 'touchstart', 'touchmove', 'touchend');
+swipeTests('mouse', /* restrictBrowers */ false, 'mousedown', 'mousemove', 'mouseup');