Compare commits
133 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 10644432ca | |||
| a0bfdd0d60 | |||
| 3624e3800f | |||
| b1ee5386d5 | |||
| ab80cd9066 | |||
| 8199f4dbde | |||
| 313d7956e4 | |||
| b9479ee73b | |||
| 769a00dc86 | |||
| 6593c2371e | |||
| 8b54524c07 | |||
| 66bb5aa41c | |||
| b186709003 | |||
| a27d827c22 | |||
| a1648a76c0 | |||
| 2bcd02dc1a | |||
| b0033a44bd | |||
| 76b755f3cb | |||
| 6303c3dcf6 | |||
| cd2cfafcab | |||
| 86d33c5f9d | |||
| eb935e6be0 | |||
| b119251827 | |||
| 5572b40b15 | |||
| 4a6c7cf8ce | |||
| 27d12340d9 | |||
| fb0c77f0b6 | |||
| 6417a3e9eb | |||
| 07e3abc7dd | |||
| b9df121655 | |||
| b5bb4a986a | |||
| 8202c4dcea | |||
| 2c8b464852 | |||
| a192c41ddc | |||
| a8fe2cc345 | |||
| e522c25fd4 | |||
| 5dbc2d65f3 | |||
| 27300072d1 | |||
| 7ffc247d0f | |||
| 8ab673d430 | |||
| b9e899c8b2 | |||
| 92f05e5a59 | |||
| e81ae1464d | |||
| de38899f74 | |||
| 729c238e19 | |||
| dc3de7fb7a | |||
| ace40d5526 | |||
| fd8997551f | |||
| d8c8b2ebb7 | |||
| f5bb34ab4a | |||
| 4b83f6ca2c | |||
| a591e8b8d3 | |||
| 6b05105c08 | |||
| 728832ec85 | |||
| f1a75a445c | |||
| c59bee5d21 | |||
| 17ecf84b90 | |||
| df8d9507aa | |||
| 6e7fbe77c9 | |||
| 3686f45398 | |||
| ad28baaa6c | |||
| 8f9c4daca5 | |||
| f0c94ea292 | |||
| 729129b461 | |||
| bf2c55ea29 | |||
| deafb5e545 | |||
| 0702aef7ee | |||
| f0ee335311 | |||
| 02169d4957 | |||
| 25d0eff3e6 | |||
| bd41bd594c | |||
| 373d7c95d9 | |||
| 4c5c762378 | |||
| d1434c999a | |||
| 8b8f6f5124 | |||
| 25082b3439 | |||
| 27b3ea4d32 | |||
| ffc32b4e42 | |||
| 80b0909927 | |||
| efbb365533 | |||
| 38e0ab9bd8 | |||
| 3c53b28cc2 | |||
| 0ba864184b | |||
| 4f9dc44f88 | |||
| f3884df0a9 | |||
| f7a4a70c28 | |||
| 8428a0adef | |||
| 14a92a5982 | |||
| 798ed3be21 | |||
| 4627410245 | |||
| 7c6026437a | |||
| 76741a9393 | |||
| 8900390add | |||
| fca6be7127 | |||
| ec9c0d76fe | |||
| fd2d6c02f9 | |||
| cfdd16157e | |||
| 06280a14b4 | |||
| 8173382c4b | |||
| 55244390c8 | |||
| fb7e05cc38 | |||
| ea94e63e35 | |||
| e7ac08a061 | |||
| 7ffe524171 | |||
| 30354c58fe | |||
| 19871d28cf | |||
| 63761fdac1 | |||
| 3d367eb7b9 | |||
| d13b4bd1f5 | |||
| c1f2c3ea83 | |||
| 6af2ff2c7d | |||
| 67919c8087 | |||
| 5cf1d89692 | |||
| 58adaa6634 | |||
| 0eadee5c24 | |||
| 6a96a8200a | |||
| 871f321f50 | |||
| bd8a912774 | |||
| a3962f0df3 | |||
| eb4afd45f7 | |||
| 1c8a7459c9 | |||
| 9cd272ac60 | |||
| 1a1ef62903 | |||
| 3e51b84bc1 | |||
| a0d6e64889 | |||
| 1d36cff22e | |||
| 56f09f0b44 | |||
| 9314719d1e | |||
| 14183833f9 | |||
| d307e77ff3 | |||
| f0d3722e07 | |||
| fb39e322ea | |||
| d1318f7dc7 |
+158
-14
@@ -1,12 +1,164 @@
|
||||
<a name="1.3.0-rc.3"></a>
|
||||
# 1.3.0-rc.3 aggressive-pacification (2014-09-23)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **ngModel:** support milliseconds in time and datetime
|
||||
([4b83f6ca](https://github.com/angular/angular.js/commit/4b83f6ca2c15bd65fe2b3894a02c04f9967fbff4),
|
||||
[#8874](https://github.com/angular/angular.js/issues/8874))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$location:** add ability to opt-out of `<base>` tag requirement in html5Mode
|
||||
([dc3de7fb](https://github.com/angular/angular.js/commit/dc3de7fb7a14c38b5c3dc7decfafb0b51d422dd1),
|
||||
[#8934](https://github.com/angular/angular.js/issues/8934))
|
||||
- **formController:** add $setUntouched to propagate untouched state
|
||||
([fd899755](https://github.com/angular/angular.js/commit/fd8997551f9ed4431f5e99d61f637139485076b9),
|
||||
[#9050](https://github.com/angular/angular.js/issues/9050))
|
||||
- **input:** support dynamic element validation
|
||||
([729c238e](https://github.com/angular/angular.js/commit/729c238e19ab27deff01448d79342ea53721bfed),
|
||||
[#4791](https://github.com/angular/angular.js/issues/4791), [#1404](https://github.com/angular/angular.js/issues/1404))
|
||||
- **ngAria:** add an ngAria module to make a11y easier
|
||||
([d1434c99](https://github.com/angular/angular.js/commit/d1434c999a66c6bb915ee1a8b091e497d288d940),
|
||||
[#5486](https://github.com/angular/angular.js/issues/5486))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **map:** use Array.prototype.map
|
||||
([a591e8b8](https://github.com/angular/angular.js/commit/a591e8b8d302efefd67bf0d5c4bad300a5f3aded))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$location:** due to [dc3de7fb](https://github.com/angular/angular.js/commit/dc3de7fb7a14c38b5c3dc7decfafb0b51d422dd1),
|
||||
The $location.html5Mode API has changed to allow enabling html5Mode by
|
||||
passing an object (as well as still supporting passing a boolean). Symmetrically, the
|
||||
method now returns an object instead of a boolean value.
|
||||
|
||||
To migrate, follow the code example below:
|
||||
|
||||
Before:
|
||||
|
||||
var mode = $locationProvider.html5Mode();
|
||||
|
||||
After:
|
||||
|
||||
var mode = $locationProvider.html5Mode().enabled;
|
||||
|
||||
Fixes #8934
|
||||
|
||||
|
||||
<a name="1.2.25"></a>
|
||||
# 1.2.25 hypnotic-gesticulation (2014-09-16)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **i18n:** fix typo at i18n generation code
|
||||
([1b6d74cc](https://github.com/angular/angular.js/commit/1b6d74cc9f7f7b7bd529abe6ce612de3ae661601))
|
||||
- **ngLocale:** Regenerate Locale Files
|
||||
([06c76694](https://github.com/angular/angular.js/commit/06c76694ac9b2280594712e6a4b46a1d5987d098))
|
||||
- **select:** update option labels when model changes
|
||||
([d89d59f4](https://github.com/angular/angular.js/commit/d89d59f453d4e28be4f595fea7e2c4ff2338351f),
|
||||
[#9025](https://github.com/angular/angular.js/issues/9025))
|
||||
|
||||
|
||||
|
||||
<a name="1.3.0-rc.2"></a>
|
||||
# 1.3.0-rc.2 tactile-perception (2014-09-16)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:** update `'@'`-bindings in controller when `bindToController` is `true`
|
||||
([e7ac08a0](https://github.com/angular/angular.js/commit/e7ac08a0619d2bdc91c125d341772b4fbc0d5a78),
|
||||
[#9052](https://github.com/angular/angular.js/issues/9052), [#9077](https://github.com/angular/angular.js/issues/9077))
|
||||
- **$parse:** ensure CSP assignable expressions have `assign()`
|
||||
([d13b4bd1](https://github.com/angular/angular.js/commit/d13b4bd1f5f2abaad00f5d1bf81f79549a8d0e46),
|
||||
[#9048](https://github.com/angular/angular.js/issues/9048))
|
||||
- **i18n:** fix typo at i18n generation code
|
||||
([eb4afd45](https://github.com/angular/angular.js/commit/eb4afd45f77d7d67744e01ce63a831c13c2b22e8))
|
||||
- **input:** always pass in the model value to `ctrl.$isEmpty`
|
||||
([3e51b84b](https://github.com/angular/angular.js/commit/3e51b84bc19f7e6acc61cb536ddcdbfed307c831),
|
||||
[#5164](https://github.com/angular/angular.js/issues/5164), [#9017](https://github.com/angular/angular.js/issues/9017))
|
||||
- **jqLite:** fix `event.stopImmediatePropagation()` so it works as expected
|
||||
([30354c58](https://github.com/angular/angular.js/commit/30354c58fe2bd371df364f7a3f55b270692a4051),
|
||||
[#4833](https://github.com/angular/angular.js/issues/4833))
|
||||
- **ngLocale:** Regenerate Locale Files
|
||||
([6a96a820](https://github.com/angular/angular.js/commit/6a96a8200aff4749bc84c44a1e8018b09d9ebdb4),
|
||||
[#8931](https://github.com/angular/angular.js/issues/8931), [#8583](https://github.com/angular/angular.js/issues/8583), [#7799](https://github.com/angular/angular.js/issues/7799))
|
||||
- **ngModel:**
|
||||
- do not reset bound date objects
|
||||
([1a1ef629](https://github.com/angular/angular.js/commit/1a1ef62903c8fdf4ceb81277d966a8eff67f0a96),
|
||||
[#6666](https://github.com/angular/angular.js/issues/6666))
|
||||
- don’t clear the model when an external validator failed
|
||||
([9314719d](https://github.com/angular/angular.js/commit/9314719d1eb5f480b877f5513f6e0e474edcb67d),
|
||||
[#8357](https://github.com/angular/angular.js/issues/8357), [#8080](https://github.com/angular/angular.js/issues/8080))
|
||||
- **ngResource:** make badcfg error message more helpful
|
||||
([a3962f0d](https://github.com/angular/angular.js/commit/a3962f0df3f9b8382b47952f9e4fcb48a4cc098b),
|
||||
[#9005](https://github.com/angular/angular.js/issues/9005), [#9010](https://github.com/angular/angular.js/issues/9010))
|
||||
- **select:** update option labels when model changes
|
||||
([46274102](https://github.com/angular/angular.js/commit/46274102454038ee7fd4543a32166e9bbbc98904),
|
||||
[#9025](https://github.com/angular/angular.js/issues/9025))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **limitTo:** support numeric input to limitTo
|
||||
([1c8a7459](https://github.com/angular/angular.js/commit/1c8a7459c90efc77b1a0987f976e3bddab4565fe),
|
||||
[#8926](https://github.com/angular/angular.js/issues/8926))
|
||||
- **ngInclude:** add template url parameter to events
|
||||
([fd2d6c02](https://github.com/angular/angular.js/commit/fd2d6c02f9654e753d3655a3377a9534f7a54de3),
|
||||
[#8453](https://github.com/angular/angular.js/issues/8453), [#8454](https://github.com/angular/angular.js/issues/8454))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$compile:** move `$$isolateBinding` creation to directive factory instead of on each link
|
||||
([56f09f0b](https://github.com/angular/angular.js/commit/56f09f0b44048b62f964d29db4d3d2630662f6ea))
|
||||
- **$parse:**
|
||||
- execute watched expressions only when the inputs change
|
||||
([fca6be71](https://github.com/angular/angular.js/commit/fca6be71274e537c7df86ae9e27a3bd1597e9ffa),
|
||||
[#9006](https://github.com/angular/angular.js/issues/9006), [#9082](https://github.com/angular/angular.js/issues/9082))
|
||||
- remove `binaryFn` and `valueFn` wrappers from filter expressions
|
||||
([67919c80](https://github.com/angular/angular.js/commit/67919c808771a9b185a9d552cd32a90748d36666))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$parse:** due to [fca6be71](https://github.com/angular/angular.js/commit/fca6be71274e537c7df86ae9e27a3bd1597e9ffa),
|
||||
all filters are assumed to be stateless functions
|
||||
|
||||
Previously it was just a good practice to make all filters stateless. Now
|
||||
it's a requirement in order for the model change-observation to pick up
|
||||
all changes.
|
||||
|
||||
If an existing filter is statefull, it can be flagged as such but keep in
|
||||
mind that this will result in a significant performance-penalty (or rather
|
||||
lost opportunity to benefit from a major perf improvement) that will
|
||||
affect the `$digest` duration.
|
||||
|
||||
To flag a filter as stateful do the following:
|
||||
|
||||
```javascript
|
||||
myApp.filter('myFilter', function() {
|
||||
function myFilter(input) { ... };
|
||||
myFilter.$stateful = true;
|
||||
return myFilter;
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
|
||||
<a name="1.3.0-rc.1"></a>
|
||||
# 1.3.0-rc.1 backyard-atomicity (2014-09-09)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:** render nested transclusion at the root of a template
|
||||
([6d1e7cdc](https://github.com/angular/angular.js/commit/6d1e7cdc51c074139639e870b66997fb0df4523f),
|
||||
[#8914](https://github.com/angular/angular.js/issues/8914), [#8925](https://github.com/angular/angular.js/issues/8925))
|
||||
- **$location:**
|
||||
- don't call toString on null values
|
||||
([c3a58a9f](https://github.com/angular/angular.js/commit/c3a58a9f34919f121587540e03ecbd51b25198d4))
|
||||
@@ -99,7 +251,7 @@
|
||||
|
||||
- **ngModelController,formController:** due to [6046e14b](https://github.com/angular/angular.js/commit/6046e14bd22491168116e61ffdf5fd3fed5f135c),
|
||||
|
||||
- `ctrl.$error` does no more contain entries for validators that were
|
||||
- `ctrl.$error` no longer contains entries for validators that were
|
||||
successful.
|
||||
- `ctrl.$setValidity` now differentiates between `true`, `false`,
|
||||
`undefined` and `null`, instead of previously only truthy vs falsy.
|
||||
@@ -121,8 +273,8 @@ Example:
|
||||
angular.module("switchChangeWorkaround", []).
|
||||
directive("onSwitchChanged", function() {
|
||||
return {
|
||||
linke: function($scope, $attrs) {
|
||||
$scope.$parent.$eval($attrs.change);
|
||||
link: function($scope, $element, $attrs) {
|
||||
$scope.$parent.$eval($attrs.onSwitchChanged);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1856,14 +2008,6 @@ this limitation, use a regular expression object as the value for the expression
|
||||
//after
|
||||
$scope.exp = /abc/i;
|
||||
|
||||
- **NgModel:** due to [f3cb2741161353f387d02725637ce4ba062a9bc0](https://github.com/angular/angular.js/commit/f3cb2741161353f387d02725637ce4ba062a9bc0),
|
||||
|
||||
#### since 1.3.0-beta.11
|
||||
|
||||
If the user enters a value and a parser or validator fails, the model will be set to `undefined`.
|
||||
This is the same behavior as in 1.2.x, but different to 1.3.0-beta.11, as there only invalid parsers
|
||||
would set the model to `undefined`, but invalid validators would not change the model.
|
||||
|
||||
- **Scope:** due to [8c6a8171](https://github.com/angular/angular.js/commit/8c6a8171f9bdaa5cdabc0cc3f7d3ce10af7b434d),
|
||||
Scope#$id is now of time number rather than string. Since the
|
||||
id is primarily being used for debugging purposes this change should not affect
|
||||
|
||||
+4
-4
@@ -54,7 +54,7 @@ For large fixes, please build and test the documentation before submitting the P
|
||||
accidentally introduced any layout or formatting issues. You should also make sure that your commit message
|
||||
is labeled "docs:" and follows the **Git Commit Guidelines** outlined below.
|
||||
|
||||
If you're just making a small change, don't worry about filing an issue first. Use the friendly blue "Improve this doc" button at the top right of the doc page to fork the repository in-place and make a quick change on the fly.
|
||||
If you're just making a small change, don't worry about filing an issue first. Use the friendly blue "Improve this doc" button at the top right of the doc page to fork the repository in-place and make a quick change on the fly. When naming the commit, it is advised to still label it according to the commit guidelines below, by starting the commit message with **docs** and referencing the filename. Since this is not obvious and some changes are made on the fly, this is not strictly necessary and we will understand if this isn't done the first few times.
|
||||
|
||||
## <a name="submit"></a> Submission Guidelines
|
||||
|
||||
@@ -66,13 +66,13 @@ Help us to maximize the effort we can spend fixing issues and adding new
|
||||
features, by not reporting duplicate issues. Providing the following information will increase the
|
||||
chances of your issue being dealt with quickly:
|
||||
|
||||
* **Overview of the issue** - if an error is being thrown a non-minified stack trace helps
|
||||
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
|
||||
* **Motivation for or Use Case** - explain why this is a bug for you
|
||||
* **Angular Version(s)** - is it a regression?
|
||||
* **Browsers and Operating System** - is this a problem with all browsers or only IE8?
|
||||
* **Reproduce the error** - provide a live example (using [Plunker][plunker] or
|
||||
* **Reproduce the Error** - provide a live example (using [Plunker][plunker] or
|
||||
[JSFiddle][jsfiddle]) or a unambiguous set of steps.
|
||||
* **Related issues** - has a similar issue been reported before?
|
||||
* **Related Issues** - has a similar issue been reported before?
|
||||
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
||||
causing the problem (line of code or commit)
|
||||
|
||||
|
||||
+11
-3
@@ -47,8 +47,7 @@ module.exports = function(grunt) {
|
||||
keepalive: true,
|
||||
middleware: function(connect, options){
|
||||
return [
|
||||
//uncomment to enable CSP
|
||||
// util.csp(),
|
||||
util.conditionalCsp(),
|
||||
util.rewrite(),
|
||||
connect.favicon('images/favicon.ico'),
|
||||
connect.static(options.base),
|
||||
@@ -74,6 +73,7 @@ module.exports = function(grunt) {
|
||||
|
||||
next();
|
||||
},
|
||||
util.conditionalCsp(),
|
||||
connect.favicon('images/favicon.ico'),
|
||||
connect.static(options.base)
|
||||
];
|
||||
@@ -153,6 +153,9 @@ module.exports = function(grunt) {
|
||||
},
|
||||
ngTouch: {
|
||||
files: { src: 'src/ngTouch/**/*.js' },
|
||||
},
|
||||
ngAria: {
|
||||
files: {src: 'src/ngAria/**/*.js'},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -220,6 +223,10 @@ module.exports = function(grunt) {
|
||||
dest: 'build/angular-cookies.js',
|
||||
src: util.wrap(files['angularModules']['ngCookies'], 'module')
|
||||
},
|
||||
aria: {
|
||||
dest: 'build/angular-aria.js',
|
||||
src: util.wrap(files['angularModules']['ngAria'], 'module')
|
||||
},
|
||||
"promises-aplus-adapter": {
|
||||
dest:'tmp/promises-aplus-adapter++.js',
|
||||
src:['src/ng/q.js','lib/promises-aplus/promises-aplus-test-adapter.js']
|
||||
@@ -236,7 +243,8 @@ module.exports = function(grunt) {
|
||||
touch: 'build/angular-touch.js',
|
||||
resource: 'build/angular-resource.js',
|
||||
route: 'build/angular-route.js',
|
||||
sanitize: 'build/angular-sanitize.js'
|
||||
sanitize: 'build/angular-sanitize.js',
|
||||
aria: 'build/angular-aria.js'
|
||||
},
|
||||
|
||||
|
||||
|
||||
@@ -6,10 +6,11 @@ use good old HTML (or HAML, Jade and friends!) as your template language and let
|
||||
syntax to express your application’s components clearly and succinctly. It automatically
|
||||
synchronizes data from your UI (view) with your JavaScript objects (model) through 2-way data
|
||||
binding. To help you structure your application better and make it easy to test, AngularJS teaches
|
||||
the browser how to do dependency injection and inversion of control. Oh yeah and it also helps with
|
||||
server-side communication, taming async callbacks with promises and deferreds; and makes client-side
|
||||
navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all:
|
||||
it makes development fun!
|
||||
the browser how to do dependency injection and inversion of control.
|
||||
|
||||
Oh yeah and it helps with server-side communication, taming async callbacks with promises and
|
||||
deferreds. It also makes client-side navigation and deeplinking with hashbang urls or HTML5 pushState a
|
||||
piece of cake. The best of all: it makes development fun!
|
||||
|
||||
* Web site: http://angularjs.org
|
||||
* Tutorial: http://docs.angularjs.org/tutorial
|
||||
|
||||
@@ -26,7 +26,6 @@ This process based on the idea of minimizing user pain
|
||||
* You can triage older issues as well
|
||||
* Triage to your heart's content
|
||||
1. Assign yourself: Pick an issue that is not assigned to anyone and assign it to you
|
||||
|
||||
1. Understandable? - verify if the description of the request is clear.
|
||||
* If not, [close it][] according to the instructions below and go to the last step.
|
||||
1. Duplicate?
|
||||
@@ -36,7 +35,6 @@ This process based on the idea of minimizing user pain
|
||||
* Label `Type: Bug`
|
||||
* Reproducible? - Steps to reproduce the bug are clear. If they are not, ask for a clarification. If there's no reply after a week, [close it][].
|
||||
* Reproducible on master? - <http://code.angularjs.org/snapshot/>
|
||||
|
||||
1. Non bugs:
|
||||
* Label `Type: Feature`, `Type: Chore`, or `Type: Perf`
|
||||
* Belongs in core? – Often new features should be implemented as a third-party module rather than an addition to the core.
|
||||
@@ -59,7 +57,6 @@ This process based on the idea of minimizing user pain
|
||||
* In rare cases, it's ok to have multiple components.
|
||||
1. Label `PRs plz!` - These issues are good targets for PRs from the open source community. Apply to issues where the problem and solution are well defined in the comments, and it's not too complex.
|
||||
1. Label `origin: google` for issues from Google
|
||||
|
||||
1. Assign a milestone:
|
||||
* Backlog - triaged fixes and features, should be the default choice
|
||||
* Current 1.x.y milestone (e.g. 1.3.0-beta-2) - regressions and urgent bugs only
|
||||
|
||||
Vendored
+9
-3
@@ -108,6 +108,9 @@ var angularFiles = {
|
||||
'src/ngTouch/directive/ngClick.js',
|
||||
'src/ngTouch/directive/ngSwipe.js'
|
||||
],
|
||||
'ngAria': [
|
||||
'src/ngAria/aria.js'
|
||||
]
|
||||
},
|
||||
|
||||
'angularScenario': [
|
||||
@@ -141,7 +144,8 @@ var angularFiles = {
|
||||
'test/ngRoute/**/*.js',
|
||||
'test/ngSanitize/**/*.js',
|
||||
'test/ngMock/*.js',
|
||||
'test/ngTouch/**/*.js'
|
||||
'test/ngTouch/**/*.js',
|
||||
'test/ngAria/*.js'
|
||||
],
|
||||
|
||||
'karma': [
|
||||
@@ -175,7 +179,8 @@ var angularFiles = {
|
||||
'test/ngRoute/**/*.js',
|
||||
'test/ngResource/*.js',
|
||||
'test/ngSanitize/**/*.js',
|
||||
'test/ngTouch/**/*.js'
|
||||
'test/ngTouch/**/*.js',
|
||||
'test/ngAria/*.js'
|
||||
],
|
||||
|
||||
'karmaJquery': [
|
||||
@@ -203,7 +208,8 @@ angularFiles['angularSrcModules'] = [].concat(
|
||||
angularFiles['angularModules']['ngRoute'],
|
||||
angularFiles['angularModules']['ngSanitize'],
|
||||
angularFiles['angularModules']['ngMock'],
|
||||
angularFiles['angularModules']['ngTouch']
|
||||
angularFiles['angularModules']['ngTouch'],
|
||||
angularFiles['angularModules']['ngAria']
|
||||
);
|
||||
|
||||
if (exports) {
|
||||
|
||||
@@ -8,15 +8,16 @@
|
||||
Large table rendered with AngularJS
|
||||
</p>
|
||||
|
||||
<div>none: <input type=radio ng-model="benchmarkType" value="none"></div>
|
||||
<div>baseline binding: <input type=radio ng-model="benchmarkType" value="baselineBinding"></div>
|
||||
<div>baseline interpolation: <input type=radio ng-model="benchmarkType" value="baselineInterpolation"></div>
|
||||
<div>ngBind: <input type=radio ng-model="benchmarkType" value="ngBind"></div>
|
||||
<div>interpolation: <input type=radio ng-model="benchmarkType" value="interpolation"></div>
|
||||
<div>ngBind + fnInvocation: <input type=radio ng-model="benchmarkType" value="ngBindFn"></div>
|
||||
<div>interpolation + fnInvocation: <input type=radio ng-model="benchmarkType" value="interpolationFn"></div>
|
||||
<div>ngBind + filter: <input type=radio ng-model="benchmarkType" value="ngBindFilter"></div>
|
||||
<div>ngBind + filter: <input type=radio ng-model="benchmarkType" value="interpolationFilter"></div>
|
||||
<div>none: <input type="radio" ng-model="benchmarkType" value="none"></div>
|
||||
<div>baseline binding: <input type="radio" ng-model="benchmarkType" value="baselineBinding"></div>
|
||||
<div>baseline interpolation: <input type="radio" ng-model="benchmarkType" value="baselineInterpolation"></div>
|
||||
<div>ngBind: <input type="radio" ng-model="benchmarkType" value="ngBind"></div>
|
||||
<div>ngBindOnce: <input type="radio" ng-model="benchmarkType" value="ngBindOnce"></div>
|
||||
<div>interpolation: <input type="radio" ng-model="benchmarkType" value="interpolation"></div>
|
||||
<div>ngBind + fnInvocation: <input type="radio" ng-model="benchmarkType" value="ngBindFn"></div>
|
||||
<div>interpolation + fnInvocation: <input type="radio" ng-model="benchmarkType" value="interpolationFn"></div>
|
||||
<div>ngBind + filter: <input type="radio" ng-model="benchmarkType" value="ngBindFilter"></div>
|
||||
<div>interpolation + filter: <input type="radio" ng-model="benchmarkType" value="interpolationFilter"></div>
|
||||
|
||||
<ng-switch on="benchmarkType">
|
||||
<baseline-binding-table ng-switch-when="baselineBinding">
|
||||
@@ -26,7 +27,17 @@
|
||||
<div ng-switch-when="ngBind">
|
||||
<h2>baseline binding</h2>
|
||||
<div ng-repeat="row in data">
|
||||
<span ng-repeat="column in row"><span ng-bind="column.i"></span>:<span ng-bind="column.j"></span>|</span>
|
||||
<span ng-repeat="column in row">
|
||||
<span ng-bind="column.i"></span>:<span ng-bind="column.j"></span>|
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="ngBindOnce">
|
||||
<h2>baseline binding once</h2>
|
||||
<div ng-repeat="row in data">
|
||||
<span ng-repeat="column in ::row">
|
||||
<span ng-bind="::column.i"></span>:<span ng-bind="::column.j"></span>|
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-switch-when="interpolation">
|
||||
|
||||
@@ -31,6 +31,11 @@
|
||||
<label for="operators">Binary/Unary operators</label>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<input type="radio" ng-model="expressionType" value="shortCircuitingOperators" id="shortCircuitingOperators">
|
||||
<label for="shortCircuitingOperators">AND/OR short-circuiting operators</label>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<input type="radio" ng-model="expressionType" value="filters" id="filters">
|
||||
<label for="filters">Filters</label>
|
||||
@@ -134,6 +139,17 @@
|
||||
<span bm-pe-watch="-rowIdx * 2 * rowIdx + rowIdx / rowIdx + 1"></span>
|
||||
</li>
|
||||
|
||||
<li ng-switch-when="shortCircuitingOperators" ng-repeat="(rowIdx, row) in ::data">
|
||||
<span bm-pe-watch="rowIdx && row.odd"></span>
|
||||
<span bm-pe-watch="row.odd && row.even"></span>
|
||||
<span bm-pe-watch="row.odd && !row.even"></span>
|
||||
<span bm-pe-watch="row.odd || row.even"></span>
|
||||
<span bm-pe-watch="row.odd || row.even || row.index"></span>
|
||||
<span bm-pe-watch="row.index === 1 || row.index === 2"></span>
|
||||
<span bm-pe-watch="row.num0 < row.num1 && row.num1 < row.num2"></span>
|
||||
<span bm-pe-watch="row.num0 < row.num1 || row.num1 < row.num2"></span>
|
||||
</li>
|
||||
|
||||
<li ng-switch-when="filters" ng-repeat="(rowIdx, row) in ::data">
|
||||
<span bm-pe-watch="rowIdx | noop"></span>
|
||||
<span bm-pe-watch="rowIdx | noop"></span>
|
||||
|
||||
@@ -316,10 +316,10 @@ iframe.example {
|
||||
}
|
||||
|
||||
.search-results-group.col-group-api { width:30%; }
|
||||
.search-results-group.col-group-guide { width:30%; }
|
||||
.search-results-group.col-group-tutorial { width:25%; }
|
||||
.search-results-group.col-group-guide,
|
||||
.search-results-group.col-group-tutorial { width:20%; }
|
||||
.search-results-group.col-group-misc,
|
||||
.search-results-group.col-group-error { float:right; clear:both; width:15% }
|
||||
.search-results-group.col-group-error { width:15%; float: right; }
|
||||
|
||||
|
||||
.search-results-group.col-group-api .search-result {
|
||||
@@ -391,7 +391,6 @@ iframe.example {
|
||||
position:fixed;
|
||||
top:120px;
|
||||
bottom:0;
|
||||
padding-bottom:120px;
|
||||
overflow:auto;
|
||||
}
|
||||
|
||||
@@ -412,6 +411,7 @@ iframe.example {
|
||||
|
||||
.main-body-grid .side-navigation {
|
||||
position:relative;
|
||||
padding-bottom:120px;
|
||||
}
|
||||
|
||||
.main-body-grid .side-navigation.ng-hide {
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var directive = {};
|
||||
var service = { value: {} };
|
||||
|
||||
var DEPENDENCIES = {
|
||||
'angular.js': 'http://code.angularjs.org/' + angular.version.full + '/angular.min.js',
|
||||
'angular-resource.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-resource.min.js',
|
||||
'angular-route.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-route.min.js',
|
||||
'angular-animate.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-animate.min.js',
|
||||
'angular-sanitize.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-sanitize.min.js',
|
||||
'angular-cookies.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-cookies.min.js'
|
||||
};
|
||||
|
||||
|
||||
function escape(text) {
|
||||
return text.
|
||||
replace(/\&/g, '&').
|
||||
replace(/\</g, '<').
|
||||
replace(/\>/g, '>').
|
||||
replace(/"/g, '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* http://stackoverflow.com/questions/451486/pre-tag-loses-line-breaks-when-setting-innerhtml-in-ie
|
||||
* http://stackoverflow.com/questions/195363/inserting-a-newline-into-a-pre-tag-ie-javascript
|
||||
*/
|
||||
function setHtmlIe8SafeWay(element, html) {
|
||||
var newElement = angular.element('<pre>' + html + '</pre>');
|
||||
|
||||
element.empty();
|
||||
element.append(newElement.contents());
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
directive.jsFiddle = function(getEmbeddedTemplate, escape, script) {
|
||||
return {
|
||||
terminal: true,
|
||||
link: function(scope, element, attr) {
|
||||
var name = '',
|
||||
stylesheet = '<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">\n',
|
||||
fields = {
|
||||
html: '',
|
||||
css: '',
|
||||
js: ''
|
||||
};
|
||||
|
||||
angular.forEach(attr.jsFiddle.split(' '), function(file, index) {
|
||||
var fileType = file.split('.')[1];
|
||||
|
||||
if (fileType == 'html') {
|
||||
if (index == 0) {
|
||||
fields[fileType] +=
|
||||
'<div ng-app' + (attr.module ? '="' + attr.module + '"' : '') + '>\n' +
|
||||
getEmbeddedTemplate(file, 2);
|
||||
} else {
|
||||
fields[fileType] += '\n\n\n <!-- CACHE FILE: ' + file + ' -->\n' +
|
||||
' <script type="text/ng-template" id="' + file + '">\n' +
|
||||
getEmbeddedTemplate(file, 4) +
|
||||
' </script>\n';
|
||||
}
|
||||
} else {
|
||||
fields[fileType] += getEmbeddedTemplate(file) + '\n';
|
||||
}
|
||||
});
|
||||
|
||||
fields.html += '</div>\n';
|
||||
|
||||
setHtmlIe8SafeWay(element,
|
||||
'<form class="jsfiddle" method="post" action="http://jsfiddle.net/api/post/library/pure/" target="_blank">' +
|
||||
hiddenField('title', 'AngularJS Example: ' + name) +
|
||||
hiddenField('css', '</style> <!-- Ugly Hack due to jsFiddle issue: http://goo.gl/BUfGZ --> \n' +
|
||||
stylesheet +
|
||||
script.angular +
|
||||
(attr.resource ? script.resource : '') +
|
||||
'<style>\n' +
|
||||
fields.css) +
|
||||
hiddenField('html', fields.html) +
|
||||
hiddenField('js', fields.js) +
|
||||
'<button class="btn btn-primary"><i class="icon-white icon-pencil"></i> Edit Me</button>' +
|
||||
'</form>');
|
||||
|
||||
function hiddenField(name, value) {
|
||||
return '<input type="hidden" name="' + name + '" value="' + escape(value) + '">';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
directive.ngSetText = ['getEmbeddedTemplate', function(getEmbeddedTemplate) {
|
||||
return {
|
||||
restrict: 'CA',
|
||||
priority: 10,
|
||||
compile: function(element, attr) {
|
||||
setHtmlIe8SafeWay(element, escape(getEmbeddedTemplate(attr.ngSetText)));
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
|
||||
directive.ngHtmlWrap = ['reindentCode', 'templateMerge', function(reindentCode, templateMerge) {
|
||||
return {
|
||||
compile: function(element, attr) {
|
||||
var properties = {
|
||||
head: '',
|
||||
module: '',
|
||||
body: element.text()
|
||||
},
|
||||
html = "<!doctype html>\n<html ng-app{{module}}>\n <head>\n{{head:4}} </head>\n <body>\n{{body:4}} </body>\n</html>";
|
||||
|
||||
angular.forEach((attr.ngHtmlWrap || '').split(' '), function(dep) {
|
||||
if (!dep) return;
|
||||
dep = DEPENDENCIES[dep] || dep;
|
||||
|
||||
var ext = dep.split(/\./).pop();
|
||||
|
||||
if (ext == 'css') {
|
||||
properties.head += '<link rel="stylesheet" href="' + dep + '" type="text/css">\n';
|
||||
} else if(ext == 'js') {
|
||||
properties.head += '<script src="' + dep + '"></script>\n';
|
||||
} else {
|
||||
properties.module = '="' + dep + '"';
|
||||
}
|
||||
});
|
||||
|
||||
setHtmlIe8SafeWay(element, escape(templateMerge(html, properties)));
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
directive.ngSetHtml = ['getEmbeddedTemplate', function(getEmbeddedTemplate) {
|
||||
return {
|
||||
restrict: 'CA',
|
||||
priority: 10,
|
||||
compile: function(element, attr) {
|
||||
setHtmlIe8SafeWay(element, getEmbeddedTemplate(attr.ngSetHtml));
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
directive.ngEvalJavascript = ['getEmbeddedTemplate', function(getEmbeddedTemplate) {
|
||||
return {
|
||||
compile: function (element, attr) {
|
||||
var fileNames = attr.ngEvalJavascript.split(' ');
|
||||
angular.forEach(fileNames, function(fileName) {
|
||||
var script = getEmbeddedTemplate(fileName);
|
||||
try {
|
||||
if (window.execScript) { // IE
|
||||
window.execScript(script || '""'); // IE complains when evaling empty string
|
||||
} else {
|
||||
window.eval(script + '//@ sourceURL=' + fileName);
|
||||
}
|
||||
} catch (e) {
|
||||
if (window.console) {
|
||||
window.console.log(script, '\n', e);
|
||||
} else {
|
||||
window.alert(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
|
||||
directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location', '$sniffer', '$animate',
|
||||
function($templateCache, $browser, docsRootScope, $location, $sniffer, $animate) {
|
||||
return {
|
||||
terminal: true,
|
||||
link: function(scope, element, attrs) {
|
||||
var modules = ['ngAnimate'],
|
||||
embedRootScope,
|
||||
deregisterEmbedRootScope;
|
||||
|
||||
modules.push(['$provide', function($provide) {
|
||||
$provide.value('$templateCache', $templateCache);
|
||||
$provide.value('$anchorScroll', angular.noop);
|
||||
$provide.value('$browser', $browser);
|
||||
$provide.value('$sniffer', $sniffer);
|
||||
$provide.value('$animate', $animate);
|
||||
$provide.provider('$location', function() {
|
||||
this.$get = ['$rootScope', function($rootScope) {
|
||||
docsRootScope.$on('$locationChangeSuccess', function(event, oldUrl, newUrl) {
|
||||
$rootScope.$broadcast('$locationChangeSuccess', oldUrl, newUrl);
|
||||
});
|
||||
return $location;
|
||||
}];
|
||||
this.html5Mode = angular.noop;
|
||||
});
|
||||
|
||||
$provide.decorator('$rootScope', ['$delegate', function($delegate) {
|
||||
embedRootScope = $delegate;
|
||||
|
||||
// Since we are teleporting the $animate service, which relies on the $$postDigestQueue
|
||||
// we need the embedded scope to use the same $$postDigestQueue as the outer scope
|
||||
embedRootScope.$$postDigestQueue = docsRootScope.$$postDigestQueue;
|
||||
|
||||
deregisterEmbedRootScope = docsRootScope.$watch(function embedRootScopeDigestWatch() {
|
||||
embedRootScope.$digest();
|
||||
});
|
||||
|
||||
return embedRootScope;
|
||||
}]);
|
||||
}]);
|
||||
if (attrs.ngEmbedApp) modules.push(attrs.ngEmbedApp);
|
||||
|
||||
element.on('click', function(event) {
|
||||
if (event.target.attributes.getNamedItem('ng-click')) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
element.on('$destroy', function() {
|
||||
deregisterEmbedRootScope();
|
||||
embedRootScope.$destroy();
|
||||
});
|
||||
|
||||
element.data('$injector', null);
|
||||
angular.bootstrap(element, modules);
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
service.reindentCode = function() {
|
||||
return function (text, spaces) {
|
||||
if (!text) return text;
|
||||
var lines = text.split(/\r?\n/);
|
||||
var prefix = ' '.substr(0, spaces || 0);
|
||||
var i;
|
||||
|
||||
// remove any leading blank lines
|
||||
while (lines.length && lines[0].match(/^\s*$/)) lines.shift();
|
||||
// remove any trailing blank lines
|
||||
while (lines.length && lines[lines.length - 1].match(/^\s*$/)) lines.pop();
|
||||
var minIndent = 999;
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
var line = lines[0];
|
||||
var reindentCode = line.match(/^\s*/)[0];
|
||||
if (reindentCode !== line && reindentCode.length < minIndent) {
|
||||
minIndent = reindentCode.length;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
lines[i] = prefix + lines[i].substring(minIndent);
|
||||
}
|
||||
lines.push('');
|
||||
return lines.join('\n');
|
||||
}
|
||||
};
|
||||
|
||||
service.templateMerge = ['reindentCode', function(indentCode) {
|
||||
return function(template, properties) {
|
||||
return template.replace(/\{\{(\w+)(?:\:(\d+))?\}\}/g, function(_, key, indent) {
|
||||
var value = properties[key];
|
||||
|
||||
if (indent) {
|
||||
value = indentCode(value, indent);
|
||||
}
|
||||
|
||||
return value == undefined ? '' : value;
|
||||
});
|
||||
};
|
||||
}];
|
||||
|
||||
service.getEmbeddedTemplate = ['reindentCode', function(reindentCode) {
|
||||
return function (id) {
|
||||
var element = document.getElementById(id);
|
||||
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return reindentCode(angular.element(element).html(), 0);
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
angular.module('bootstrapPrettify', []).directive(directive).factory(service);
|
||||
@@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
/* jshint browser: true */
|
||||
/* global importScripts, onmessage: true, postMessage, lunr */
|
||||
|
||||
// Load up the lunr library
|
||||
importScripts('../components/lunr.js-0.4.2/lunr.min.js');
|
||||
|
||||
// Create the lunr index - the docs should be an array of object, each object containing
|
||||
// the path and search terms for a page
|
||||
var index = lunr(function() {
|
||||
this.ref('path');
|
||||
this.field('titleWords', {boost: 50});
|
||||
this.field('members', { boost: 40});
|
||||
this.field('keywords', { boost : 20 });
|
||||
});
|
||||
|
||||
// Retrieve the searchData which contains the information about each page to be indexed
|
||||
var searchData = {};
|
||||
var searchDataRequest = new XMLHttpRequest();
|
||||
searchDataRequest.onload = function() {
|
||||
|
||||
// Store the pages data to be used in mapping query results back to pages
|
||||
searchData = JSON.parse(this.responseText);
|
||||
// Add search terms from each page to the search index
|
||||
searchData.forEach(function(page) {
|
||||
index.add(page);
|
||||
});
|
||||
postMessage({ e: 'index-ready' });
|
||||
};
|
||||
searchDataRequest.open('GET', 'search-data.json');
|
||||
searchDataRequest.send();
|
||||
|
||||
// The worker receives a message everytime the web app wants to query the index
|
||||
onmessage = function(oEvent) {
|
||||
var q = oEvent.data.q;
|
||||
var hits = index.search(q);
|
||||
var results = [];
|
||||
// Only return the array of paths to pages
|
||||
hits.forEach(function(hit) {
|
||||
results.push(hit.ref);
|
||||
});
|
||||
// The results of the query are sent back to the web app via a new message
|
||||
postMessage({ e: 'query-ready', q: q, d: results });
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"extends": "../../../.jshintrc-base",
|
||||
|
||||
"globals": {
|
||||
|
||||
/* jasmine / karma */
|
||||
"it": false,
|
||||
"iit": false,
|
||||
"describe": false,
|
||||
"ddescribe": false,
|
||||
"beforeEach": false,
|
||||
"afterEach": false,
|
||||
"expect": false,
|
||||
"jasmine": false,
|
||||
"spyOn": false,
|
||||
"waits": false,
|
||||
"waitsFor": false,
|
||||
"runs": false,
|
||||
"dump": false,
|
||||
|
||||
/* e2e */
|
||||
"browser": false,
|
||||
"element": false,
|
||||
"by": false,
|
||||
|
||||
/* testabilityPatch / matchers */
|
||||
"inject": false,
|
||||
"module": false,
|
||||
"dealoc": false,
|
||||
"_jQuery": false,
|
||||
"_jqLiteMode": false,
|
||||
"sortedHtml": false,
|
||||
"childrenTagsOf": false,
|
||||
"assertHidden": false,
|
||||
"assertVisible": false,
|
||||
"provideLog": false,
|
||||
"spyOnlyCallsWithArgs": false,
|
||||
"createMockStyleSheet": false,
|
||||
"browserTrigger": false,
|
||||
"jqLiteCacheSize": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
describe("doc.angularjs.org", function() {
|
||||
|
||||
describe("API pages", function() {
|
||||
|
||||
it("should display links to code on GitHub", function() {
|
||||
browser.get('index-debug.html#!/api/ng/service/$http');
|
||||
expect(element(by.css('.improve-docs')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/edit\/.+\/src\/ng\/http\.js/);
|
||||
|
||||
browser.get('index-debug.html#!/api/ng/service/$http');
|
||||
expect(element(by.css('.view-source')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/tree\/.+\/src\/ng\/http\.js#L\d+/);
|
||||
});
|
||||
|
||||
it('should change the page content when clicking a link to a service', function () {
|
||||
browser.get('');
|
||||
|
||||
var ngBindLink = element(by.css('.definition-table td a[href="api/ng/directive/ngClick"]'));
|
||||
ngBindLink.click();
|
||||
|
||||
var pageBody = element(by.css('h1'));
|
||||
expect(pageBody.getText()).toEqual('ngClick');
|
||||
});
|
||||
|
||||
|
||||
it('should show the functioning input directive example', function () {
|
||||
browser.get('index-debug.html#!/api/ng/directive/input');
|
||||
|
||||
// Ensure that the page is loaded before trying to switch frames.
|
||||
browser.waitForAngular();
|
||||
|
||||
browser.switchTo().frame('example-input-directive');
|
||||
|
||||
var nameInput = element(by.model('user.name'));
|
||||
nameInput.sendKeys('!!!');
|
||||
|
||||
var code = element.all(by.css('tt')).first();
|
||||
expect(code.getText()).toContain('guest!!!');
|
||||
});
|
||||
|
||||
it("should trim indentation from code blocks", function() {
|
||||
browser.get('index-debug.html#!/api/ng/type/$rootScope.Scope');
|
||||
|
||||
var codeBlocks = element.all(by.css('pre > code.lang-js'));
|
||||
codeBlocks.each(function(codeBlock) {
|
||||
var firstSpan = codeBlock.all(by.css('span')).first();
|
||||
expect(firstSpan.getText()).not.toMatch(/^\W+$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
'use strict';
|
||||
|
||||
describe("provider pages", function() {
|
||||
|
||||
it("should show the related service", function() {
|
||||
browser.get('index-debug.html#!/api/ng/provider/$compileProvider');
|
||||
var serviceLink = element.all(by.css('ol.api-profile-header-structure li a')).first();
|
||||
expect(serviceLink.getText()).toEqual('- $compile');
|
||||
expect(serviceLink.getAttribute('href')).toMatch(/api\/ng\/service\/\$compile/);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
describe("service pages", function() {
|
||||
|
||||
it("should show the related provider if there is one", function() {
|
||||
browser.get('index-debug.html#!/api/ng/service/$compile');
|
||||
var providerLink = element.all(by.css('ol.api-profile-header-structure li a')).first();
|
||||
expect(providerLink.getText()).toEqual('- $compileProvider');
|
||||
expect(providerLink.getAttribute('href')).toMatch(/api\/ng\/provider\/\$compileProvider/);
|
||||
|
||||
browser.get('index-debug.html#!/api/ng/service/$q');
|
||||
providerLink = element.all(by.css('ol.api-profile-header-structure li a')).first();
|
||||
expect(providerLink.getText()).not.toEqual('- $qProvider');
|
||||
expect(providerLink.getAttribute('href')).not.toMatch(/api\/ng\/provider\/\$compileProvider/);
|
||||
});
|
||||
|
||||
it("should show parameter defaults", function() {
|
||||
browser.get('index-debug.html#!/api/ng/service/$timeout');
|
||||
expect(element.all(by.css('.input-arguments p em')).first().getText()).toContain('(default: 0)');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -49,21 +49,6 @@ describe('docs.angularjs.org', function () {
|
||||
});
|
||||
|
||||
|
||||
it('should show the functioning input directive example', function () {
|
||||
browser.get('index-debug.html#!/api/ng/directive/input');
|
||||
|
||||
// Ensure that the page is loaded before trying to switch frames.
|
||||
browser.waitForAngular();
|
||||
|
||||
browser.switchTo().frame('example-input-directive');
|
||||
|
||||
var nameInput = element(by.model('user.name'));
|
||||
nameInput.sendKeys('!!!');
|
||||
|
||||
var code = element.all(by.css('tt')).first();
|
||||
expect(code.getText()).toContain('guest!!!');
|
||||
});
|
||||
|
||||
|
||||
it('should be resilient to trailing slashes', function() {
|
||||
browser.get('index-debug.html#!/api/ng/function/angular.noop/');
|
||||
@@ -91,27 +76,11 @@ describe('docs.angularjs.org', function () {
|
||||
expect(element(by.css('.minerr-errmsg')).getText()).toEqual("Argument 'Missing' is not a function, got undefined");
|
||||
});
|
||||
|
||||
|
||||
it("should display links to code on GitHub", function() {
|
||||
it("should display an error if the page does not exist", function() {
|
||||
browser.get('index-debug.html#!/api/does/not/exist');
|
||||
expect(element(by.css('h1')).getText()).toBe('Oops!');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("templates", function() {
|
||||
it("should show parameter defaults", function() {
|
||||
browser.get('index-debug.html#!/api/ng/service/$timeout');
|
||||
expect(element.all(by.css('.input-arguments p em')).first().getText()).toContain('(default: 0)');
|
||||
});
|
||||
});
|
||||
|
||||
describe("API pages", function() {
|
||||
it("should display links to code on GitHub", function() {
|
||||
browser.get('index-debug.html#!/api/ng/service/$http');
|
||||
expect(element(by.css('.improve-docs')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/edit\/.+\/src\/ng\/http\.js/);
|
||||
|
||||
browser.get('index-debug.html#!/api/ng/service/$http');
|
||||
expect(element(by.css('.view-source')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/tree\/.+\/src\/ng\/http\.js#L\d+/);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+3
-3
@@ -6,6 +6,7 @@ angular.module('docsApp', [
|
||||
'DocsController',
|
||||
'versionsData',
|
||||
'pagesData',
|
||||
'navData',
|
||||
'directives',
|
||||
'errors',
|
||||
'examples',
|
||||
@@ -13,11 +14,10 @@ angular.module('docsApp', [
|
||||
'tutorials',
|
||||
'versions',
|
||||
'bootstrap',
|
||||
'bootstrapPrettify',
|
||||
'ui.bootstrap.dropdown'
|
||||
])
|
||||
|
||||
|
||||
.config(function($locationProvider) {
|
||||
.config(['$locationProvider', function($locationProvider) {
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
});
|
||||
}]);
|
||||
|
||||
+9
-74
@@ -6,31 +6,10 @@ angular.module('DocsController', [])
|
||||
function($scope, $rootScope, $location, $window, $cookies, openPlunkr,
|
||||
NG_PAGES, NG_NAVIGATION, NG_VERSION) {
|
||||
|
||||
|
||||
$scope.openPlunkr = openPlunkr;
|
||||
|
||||
$scope.docsVersion = NG_VERSION.isSnapshot ? 'snapshot' : NG_VERSION.version;
|
||||
|
||||
$scope.fold = function(url) {
|
||||
if(url) {
|
||||
$scope.docs_fold = '/notes/' + url;
|
||||
if(/\/build/.test($window.location.href)) {
|
||||
$scope.docs_fold = '/build/docs' + $scope.docs_fold;
|
||||
}
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
else {
|
||||
$scope.docs_fold = null;
|
||||
}
|
||||
};
|
||||
var OFFLINE_COOKIE_NAME = 'ng-offline',
|
||||
INDEX_PATH = /^(\/|\/index[^\.]*.html)$/;
|
||||
|
||||
|
||||
/**********************************
|
||||
Publish methods
|
||||
***********************************/
|
||||
|
||||
$scope.navClass = function(navItem) {
|
||||
return {
|
||||
active: navItem.href && this.currentPage && this.currentPage.path,
|
||||
@@ -38,55 +17,22 @@ angular.module('DocsController', [])
|
||||
};
|
||||
};
|
||||
|
||||
$scope.afterPartialLoaded = function() {
|
||||
|
||||
|
||||
$scope.$on('$includeContentLoaded', function() {
|
||||
var pagePath = $scope.currentPage ? $scope.currentPage.path : $location.path();
|
||||
$window._gaq.push(['_trackPageview', pagePath]);
|
||||
};
|
||||
|
||||
/** stores a cookie that is used by apache to decide which manifest ot send */
|
||||
$scope.enableOffline = function() {
|
||||
//The cookie will be good for one year!
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime()+(365*24*60*60*1000));
|
||||
var expires = "; expires="+date.toGMTString();
|
||||
var value = angular.version.full;
|
||||
document.cookie = OFFLINE_COOKIE_NAME + "="+value+expires+"; path=" + $location.path;
|
||||
|
||||
//force the page to reload so server can serve new manifest file
|
||||
window.location.reload(true);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**********************************
|
||||
Watches
|
||||
***********************************/
|
||||
|
||||
});
|
||||
|
||||
$scope.$watch(function docsPathWatch() {return $location.path(); }, function docsPathWatchAction(path) {
|
||||
|
||||
var currentPage = $scope.currentPage = NG_PAGES[path];
|
||||
if ( !currentPage && path.charAt(0)==='/' ) {
|
||||
// Strip off leading slash
|
||||
path = path.substr(1);
|
||||
}
|
||||
|
||||
currentPage = $scope.currentPage = NG_PAGES[path];
|
||||
if ( !currentPage && path.charAt(path.length-1) === '/' && path.length > 1 ) {
|
||||
// Strip off trailing slash
|
||||
path = path.substr(0, path.length-1);
|
||||
}
|
||||
|
||||
currentPage = $scope.currentPage = NG_PAGES[path];
|
||||
if ( !currentPage && /\/index$/.test(path) ) {
|
||||
// Strip off index from the end
|
||||
path = path.substr(0, path.length - 6);
|
||||
}
|
||||
path = path.replace(/^\/?(.+?)(\/index)?\/?$/, '$1');
|
||||
|
||||
currentPage = $scope.currentPage = NG_PAGES[path];
|
||||
|
||||
if ( currentPage ) {
|
||||
$scope.currentArea = currentPage && NG_NAVIGATION[currentPage.area];
|
||||
$scope.partialPath = 'partials/' + path + '.html';
|
||||
$scope.currentArea = NG_NAVIGATION[currentPage.area];
|
||||
var pathParts = currentPage.path.split('/');
|
||||
var breadcrumb = $scope.breadcrumb = [];
|
||||
var breadcrumbPath = '';
|
||||
@@ -98,6 +44,7 @@ angular.module('DocsController', [])
|
||||
} else {
|
||||
$scope.currentArea = NG_NAVIGATION['api'];
|
||||
$scope.breadcrumb = [];
|
||||
$scope.partialPath = 'Error404.html';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -107,24 +54,12 @@ angular.module('DocsController', [])
|
||||
|
||||
$scope.versionNumber = angular.version.full;
|
||||
$scope.version = angular.version.full + " " + angular.version.codeName;
|
||||
$scope.subpage = false;
|
||||
$scope.offlineEnabled = ($cookies[OFFLINE_COOKIE_NAME] == angular.version.full);
|
||||
$scope.futurePartialTitle = null;
|
||||
$scope.loading = 0;
|
||||
$scope.$cookies = $cookies;
|
||||
|
||||
$cookies.platformPreference = $cookies.platformPreference || 'gitUnix';
|
||||
|
||||
var INDEX_PATH = /^(\/|\/index[^\.]*.html)$/;
|
||||
if (!$location.path() || INDEX_PATH.test($location.path())) {
|
||||
$location.path('/api').replace();
|
||||
}
|
||||
|
||||
// bind escape to hash reset callback
|
||||
angular.element(window).on('keydown', function(e) {
|
||||
if (e.keyCode === 27) {
|
||||
$scope.$apply(function() {
|
||||
$scope.subpage = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}]);
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
angular.module('docsApp.navigationService', [])
|
||||
|
||||
.factory('navigationService', function($window) {
|
||||
var service = {
|
||||
currentPage: null,
|
||||
currentVersion: null,
|
||||
changePage: function(newPage) {
|
||||
|
||||
},
|
||||
changeVersion: function(newVersion) {
|
||||
|
||||
//TODO =========
|
||||
// var currentPagePath = '';
|
||||
|
||||
// // preserve URL path when switching between doc versions
|
||||
// if (angular.isObject($rootScope.currentPage) && $rootScope.currentPage.section && $rootScope.currentPage.id) {
|
||||
// currentPagePath = '/' + $rootScope.currentPage.section + '/' + $rootScope.currentPage.id;
|
||||
// }
|
||||
|
||||
// $window.location = version.url + currentPagePath;
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
+123
-64
@@ -10,22 +10,35 @@ angular.module('search', [])
|
||||
$scope.search = function(q) {
|
||||
var MIN_SEARCH_LENGTH = 2;
|
||||
if(q.length >= MIN_SEARCH_LENGTH) {
|
||||
var results = docsSearch(q);
|
||||
var totalAreas = 0;
|
||||
for(var i in results) {
|
||||
++totalAreas;
|
||||
}
|
||||
if(totalAreas > 0) {
|
||||
$scope.colClassName = 'cols-' + totalAreas;
|
||||
}
|
||||
$scope.hasResults = totalAreas > 0;
|
||||
$scope.results = results;
|
||||
docsSearch(q).then(function(hits) {
|
||||
var results = {};
|
||||
angular.forEach(hits, function(hit) {
|
||||
var area = hit.area;
|
||||
|
||||
var limit = (area == 'api') ? 40 : 14;
|
||||
results[area] = results[area] || [];
|
||||
if(results[area].length < limit) {
|
||||
results[area].push(hit);
|
||||
}
|
||||
});
|
||||
|
||||
var totalAreas = 0;
|
||||
for(var i in results) {
|
||||
++totalAreas;
|
||||
}
|
||||
if(totalAreas > 0) {
|
||||
$scope.colClassName = 'cols-' + totalAreas;
|
||||
}
|
||||
$scope.hasResults = totalAreas > 0;
|
||||
$scope.results = results;
|
||||
});
|
||||
}
|
||||
else {
|
||||
clearResults();
|
||||
}
|
||||
if(!$scope.$$phase) $scope.$apply();
|
||||
};
|
||||
|
||||
$scope.submit = function() {
|
||||
var result;
|
||||
for(var i in $scope.results) {
|
||||
@@ -39,78 +52,124 @@ angular.module('search', [])
|
||||
$scope.hideResults();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.hideResults = function() {
|
||||
clearResults();
|
||||
$scope.q = '';
|
||||
};
|
||||
}])
|
||||
|
||||
.controller('Error404SearchCtrl', ['$scope', '$location', 'docsSearch', function($scope, $location, docsSearch) {
|
||||
$scope.results = docsSearch($location.path().split(/[\/\.:]/).pop());
|
||||
|
||||
.controller('Error404SearchCtrl', ['$scope', '$location', 'docsSearch',
|
||||
function($scope, $location, docsSearch) {
|
||||
docsSearch($location.path().split(/[\/\.:]/).pop()).then(function(results) {
|
||||
$scope.results = {};
|
||||
angular.forEach(results, function(result) {
|
||||
var area = $scope.results[result.area] || [];
|
||||
area.push(result);
|
||||
$scope.results[result.area] = area;
|
||||
});
|
||||
});
|
||||
}])
|
||||
|
||||
.factory('lunrSearch', function() {
|
||||
return function(properties) {
|
||||
if (window.RUNNING_IN_NG_TEST_RUNNER) return null;
|
||||
|
||||
var engine = lunr(properties);
|
||||
return {
|
||||
store : function(values) {
|
||||
engine.add(values);
|
||||
},
|
||||
search : function(q) {
|
||||
return engine.search(q);
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.provider('docsSearch', function() {
|
||||
|
||||
.factory('docsSearch', ['$rootScope','lunrSearch', 'NG_PAGES',
|
||||
function($rootScope, lunrSearch, NG_PAGES) {
|
||||
if (window.RUNNING_IN_NG_TEST_RUNNER) {
|
||||
return null;
|
||||
}
|
||||
// This version of the service builds the index in the current thread,
|
||||
// which blocks rendering and other browser activities.
|
||||
// It should only be used where the browser does not support WebWorkers
|
||||
function localSearchFactory($http, $timeout, NG_PAGES) {
|
||||
|
||||
var index = lunrSearch(function() {
|
||||
this.ref('id');
|
||||
this.field('title', {boost: 50});
|
||||
this.field('members', { boost: 40});
|
||||
this.field('keywords', { boost : 20 });
|
||||
});
|
||||
console.log('Using Local Search Index');
|
||||
|
||||
angular.forEach(NG_PAGES, function(page, key) {
|
||||
if(page.searchTerms) {
|
||||
index.store({
|
||||
id : key,
|
||||
title : page.searchTerms.titleWords,
|
||||
keywords : page.searchTerms.keywords,
|
||||
members : page.searchTerms.members
|
||||
// Create the lunr index
|
||||
var index = lunr(function() {
|
||||
this.ref('path');
|
||||
this.field('titleWords', {boost: 50});
|
||||
this.field('members', { boost: 40});
|
||||
this.field('keywords', { boost : 20 });
|
||||
});
|
||||
|
||||
// Delay building the index by loading the data asynchronously
|
||||
var indexReadyPromise = $http.get('js/search-data.json').then(function(response) {
|
||||
var searchData = response.data;
|
||||
// Delay building the index for 500ms to allow the page to render
|
||||
return $timeout(function() {
|
||||
// load the page data into the index
|
||||
angular.forEach(searchData, function(page) {
|
||||
index.add(page);
|
||||
});
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// The actual service is a function that takes a query string and
|
||||
// returns a promise to the search results
|
||||
// (In this case we just resolve the promise immediately as it is not
|
||||
// inherently an async process)
|
||||
return function(q) {
|
||||
return indexReadyPromise.then(function() {
|
||||
var hits = index.search(q);
|
||||
var results = [];
|
||||
angular.forEach(hits, function(hit) {
|
||||
results.push(NG_PAGES[hit.ref]);
|
||||
});
|
||||
return results;
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
localSearchFactory.$inject = ['$http', '$timeout', 'NG_PAGES'];
|
||||
|
||||
return function(q) {
|
||||
var results = {
|
||||
api : [],
|
||||
tutorial : [],
|
||||
guide : [],
|
||||
error : [],
|
||||
misc : []
|
||||
// This version of the service builds the index in a WebWorker,
|
||||
// which does not block rendering and other browser activities.
|
||||
// It should only be used where the browser does support WebWorkers
|
||||
function webWorkerSearchFactory($q, $rootScope, NG_PAGES) {
|
||||
|
||||
console.log('Using WebWorker Search Index')
|
||||
|
||||
var searchIndex = $q.defer();
|
||||
var results;
|
||||
|
||||
var worker = new Worker('js/search-worker.js');
|
||||
|
||||
// The worker will send us a message in two situations:
|
||||
// - when the index has been built, ready to run a query
|
||||
// - when it has completed a search query and the results are available
|
||||
worker.onmessage = function(oEvent) {
|
||||
$rootScope.$apply(function() {
|
||||
|
||||
switch(oEvent.data.e) {
|
||||
case 'index-ready':
|
||||
searchIndex.resolve();
|
||||
break;
|
||||
case 'query-ready':
|
||||
var pages = oEvent.data.d.map(function(path) {
|
||||
return NG_PAGES[path];
|
||||
});
|
||||
results.resolve(pages);
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
angular.forEach(index.search(q), function(result) {
|
||||
var key = result.ref;
|
||||
var item = NG_PAGES[key];
|
||||
var area = item.area;
|
||||
item.path = key;
|
||||
|
||||
var limit = area == 'api' ? 40 : 14;
|
||||
if(results[area].length < limit) {
|
||||
results[area].push(item);
|
||||
}
|
||||
});
|
||||
return results;
|
||||
// The actual service is a function that takes a query string and
|
||||
// returns a promise to the search results
|
||||
return function(q) {
|
||||
|
||||
// We only run the query once the index is ready
|
||||
return searchIndex.promise.then(function() {
|
||||
|
||||
results = $q.defer();
|
||||
worker.postMessage({ q: q });
|
||||
return results.promise;
|
||||
});
|
||||
};
|
||||
}
|
||||
webWorkerSearchFactory.$inject = ['$q', '$rootScope', 'NG_PAGES'];
|
||||
|
||||
return {
|
||||
$get: window.Worker ? webWorkerSearchFactory : localSearchFactory
|
||||
};
|
||||
}])
|
||||
})
|
||||
|
||||
.directive('focused', function($timeout) {
|
||||
return function(scope, element, attrs) {
|
||||
|
||||
+15
-16
@@ -1,6 +1,6 @@
|
||||
angular.module('tutorials', [])
|
||||
|
||||
.directive('docTutorialNav', function(templateMerge) {
|
||||
.directive('docTutorialNav', function() {
|
||||
var pages = [
|
||||
'',
|
||||
'step_00', 'step_01', 'step_02', 'step_03', 'step_04',
|
||||
@@ -8,23 +8,22 @@ angular.module('tutorials', [])
|
||||
'step_10', 'step_11', 'step_12', 'the_end'
|
||||
];
|
||||
return {
|
||||
compile: function(element, attrs) {
|
||||
var seq = 1 * attrs.docTutorialNav,
|
||||
props = {
|
||||
seq: seq,
|
||||
prev: pages[seq],
|
||||
next: pages[2 + seq],
|
||||
diffLo: seq ? (seq - 1): '0~1',
|
||||
diffHi: seq
|
||||
};
|
||||
scope: {},
|
||||
template:
|
||||
'<a ng-href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-step-backward"></i> Previous</li></a>\n' +
|
||||
'<a ng-href="http://angular.github.io/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="glyphicon glyphicon-play"></i> Live Demo</li></a>\n' +
|
||||
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-search"></i> Code Diff</li></a>\n' +
|
||||
'<a ng-href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="glyphicon glyphicon-step-forward"></i></li></a>',
|
||||
link: function(scope, element, attrs) {
|
||||
var seq = 1 * attrs.docTutorialNav;
|
||||
scope.seq = seq;
|
||||
scope.prev = pages[seq];
|
||||
scope.next = pages[2 + seq];
|
||||
scope.diffLo = seq ? (seq - 1): '0~1';
|
||||
scope.diffHi = seq;
|
||||
|
||||
element.addClass('btn-group');
|
||||
element.addClass('tutorial-nav');
|
||||
element.append(templateMerge(
|
||||
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-step-backward"></i> Previous</li></a>\n' +
|
||||
'<a href="http://angular.github.io/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="glyphicon glyphicon-play"></i> Live Demo</li></a>\n' +
|
||||
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-search"></i> Code Diff</li></a>\n' +
|
||||
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="glyphicon glyphicon-step-forward"></i></li></a>', props));
|
||||
}
|
||||
};
|
||||
})
|
||||
@@ -47,4 +46,4 @@ angular.module('tutorials', [])
|
||||
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{step ? (step - 1): \'0~1\'}}...step-{{step}}">GitHub</a>\n' +
|
||||
'</p>'
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,3 @@
|
||||
"use strict";
|
||||
|
||||
angular.module('versions', [])
|
||||
|
||||
.controller('DocsVersionsCtrl', ['$scope', '$location', '$window', 'NG_VERSIONS', function($scope, $location, $window, NG_VERSIONS) {
|
||||
|
||||
@@ -19,7 +19,7 @@ describe("DocsController", function() {
|
||||
it("should update the Google Analytics with currentPage path if currentPage exists", inject(function($window) {
|
||||
$window._gaq = [];
|
||||
$scope.currentPage = { path: 'a/b/c' };
|
||||
$scope.afterPartialLoaded();
|
||||
$scope.$broadcast('$includeContentLoaded');
|
||||
expect($window._gaq.pop()).toEqual(['_trackPageview', 'a/b/c']);
|
||||
}));
|
||||
|
||||
@@ -27,7 +27,7 @@ describe("DocsController", function() {
|
||||
it("should update the Google Analytics with $location.path if currentPage is missing", inject(function($window, $location) {
|
||||
$window._gaq = [];
|
||||
spyOn($location, 'path').andReturn('x/y/z');
|
||||
$scope.afterPartialLoaded();
|
||||
$scope.$broadcast('$includeContentLoaded');
|
||||
expect($window._gaq.pop()).toEqual(['_trackPageview', 'x/y/z']);
|
||||
}));
|
||||
});
|
||||
|
||||
+148
-32
@@ -1,44 +1,160 @@
|
||||
var _ = require('lodash');
|
||||
"use strict";
|
||||
|
||||
var path = require('canonical-path');
|
||||
var packagePath = __dirname;
|
||||
|
||||
var basePackage = require('dgeni-packages/ngdoc');
|
||||
var examplesPackage = require('dgeni-packages/examples');
|
||||
var Package = require('dgeni').Package;
|
||||
|
||||
module.exports = function(config) {
|
||||
// Create and export a new Dgeni package called dgeni-example. This package depends upon
|
||||
// the jsdoc and nunjucks packages defined in the dgeni-packages npm module.
|
||||
module.exports = new Package('angularjs', [
|
||||
require('dgeni-packages/ngdoc'),
|
||||
require('dgeni-packages/nunjucks'),
|
||||
require('dgeni-packages/examples')
|
||||
])
|
||||
|
||||
config = basePackage(config);
|
||||
config = examplesPackage(config);
|
||||
|
||||
config.append('processing.processors', [
|
||||
require('./processors/git-data'),
|
||||
require('./processors/error-docs'),
|
||||
require('./processors/keywords'),
|
||||
require('./processors/versions-data'),
|
||||
require('./processors/pages-data'),
|
||||
require('./processors/protractor-generate'),
|
||||
require('./processors/index-page'),
|
||||
require('./processors/debug-dump')
|
||||
]);
|
||||
.factory(require('./services/errorNamespaceMap'))
|
||||
.factory(require('./services/getMinerrInfo'))
|
||||
.factory(require('./services/getVersion'))
|
||||
.factory(require('./services/gitData'))
|
||||
|
||||
config.append('processing.tagDefinitions', [
|
||||
require('./tag-defs/tutorial-step'),
|
||||
require('./tag-defs/sortOrder')
|
||||
]);
|
||||
.factory(require('./services/deployments/debug'))
|
||||
.factory(require('./services/deployments/default'))
|
||||
.factory(require('./services/deployments/jquery'))
|
||||
.factory(require('./services/deployments/production'))
|
||||
|
||||
config.append('processing.defaultTagTransforms', [
|
||||
require('dgeni-packages/jsdoc/tag-defs/transforms/trim-whitespace')
|
||||
]);
|
||||
.factory(require('./inline-tag-defs/type'))
|
||||
|
||||
config.append('processing.inlineTagDefinitions', [
|
||||
require('./inline-tag-defs/type')
|
||||
]);
|
||||
|
||||
config.set('processing.search.ignoreWordsFile', path.resolve(packagePath, 'ignore.words'));
|
||||
.processor(require('./processors/error-docs'))
|
||||
.processor(require('./processors/index-page'))
|
||||
.processor(require('./processors/keywords'))
|
||||
.processor(require('./processors/pages-data'))
|
||||
.processor(require('./processors/versions-data'))
|
||||
|
||||
config.prepend('rendering.templateFolders', [
|
||||
path.resolve(packagePath, 'templates')
|
||||
]);
|
||||
|
||||
return config;
|
||||
};
|
||||
.config(function(dgeni, log, readFilesProcessor, writeFilesProcessor) {
|
||||
|
||||
dgeni.stopOnValidationError = true;
|
||||
dgeni.stopOnProcessingError = true;
|
||||
|
||||
log.level = 'info';
|
||||
|
||||
readFilesProcessor.basePath = path.resolve(__dirname,'../..');
|
||||
readFilesProcessor.sourceFiles = [
|
||||
{ include: 'src/**/*.js', basePath: 'src' },
|
||||
{ include: 'docs/content/**/*.ngdoc', basePath: 'docs/content' }
|
||||
];
|
||||
|
||||
writeFilesProcessor.outputFolder = 'build/docs';
|
||||
|
||||
})
|
||||
|
||||
|
||||
.config(function(parseTagsProcessor) {
|
||||
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/tutorial-step'));
|
||||
parseTagsProcessor.tagDefinitions.push(require('./tag-defs/sortOrder'));
|
||||
})
|
||||
|
||||
|
||||
.config(function(inlineTagProcessor, typeInlineTagDef) {
|
||||
inlineTagProcessor.inlineTagDefinitions.push(typeInlineTagDef);
|
||||
})
|
||||
|
||||
|
||||
.config(function(templateFinder, renderDocsProcessor, gitData) {
|
||||
templateFinder.templateFolders.unshift(path.resolve(packagePath, 'templates'));
|
||||
renderDocsProcessor.extraData.git = gitData;
|
||||
})
|
||||
|
||||
|
||||
.config(function(computePathsProcessor, computeIdsProcessor) {
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['error'],
|
||||
pathTemplate: 'error/${namespace}/${name}',
|
||||
outputPathTemplate: 'partials/error/${namespace}/${name}.html'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['errorNamespace'],
|
||||
pathTemplate: 'error/${name}',
|
||||
outputPathTemplate: 'partials/error/${name}.html'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['overview', 'tutorial'],
|
||||
getPath: function(doc) {
|
||||
var docPath = path.dirname(doc.fileInfo.relativePath);
|
||||
if ( doc.fileInfo.baseName !== 'index' ) {
|
||||
docPath = path.join(docPath, doc.fileInfo.baseName);
|
||||
}
|
||||
return docPath;
|
||||
},
|
||||
outputPathTemplate: 'partials/${path}.html'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['e2e-test'],
|
||||
getPath: function() {},
|
||||
outputPathTemplate: 'ptore2e/${example.id}/${deployment.name}_test.js'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['indexPage'],
|
||||
getPath: function() {},
|
||||
outputPathTemplate: '${id}.html'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['module' ],
|
||||
pathTemplate: '${area}/${name}',
|
||||
outputPathTemplate: 'partials/${area}/${name}.html'
|
||||
});
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['componentGroup' ],
|
||||
pathTemplate: '${area}/${moduleName}/${groupType}',
|
||||
outputPathTemplate: 'partials/${area}/${moduleName}/${groupType}.html'
|
||||
});
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: ['overview', 'tutorial', 'e2e-test', 'indexPage'],
|
||||
getId: function(doc) { return doc.fileInfo.baseName; },
|
||||
getAliases: function(doc) { return [doc.id]; }
|
||||
});
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: ['error', 'errorNamespace'],
|
||||
getId: function(doc) { return 'error:' + doc.name; },
|
||||
getAliases: function(doc) { return [doc.id]; }
|
||||
});
|
||||
})
|
||||
|
||||
|
||||
.config(function(
|
||||
generateIndexPagesProcessor,
|
||||
generateProtractorTestsProcessor,
|
||||
generateExamplesProcessor,
|
||||
debugDeployment, defaultDeployment,
|
||||
jqueryDeployment, productionDeployment) {
|
||||
|
||||
generateIndexPagesProcessor.deployments = [
|
||||
debugDeployment,
|
||||
defaultDeployment,
|
||||
jqueryDeployment,
|
||||
productionDeployment
|
||||
];
|
||||
|
||||
generateProtractorTestsProcessor.deployments = [
|
||||
defaultDeployment,
|
||||
jqueryDeployment
|
||||
];
|
||||
|
||||
generateExamplesProcessor.deployments = [
|
||||
debugDeployment,
|
||||
defaultDeployment,
|
||||
jqueryDeployment,
|
||||
productionDeployment
|
||||
];
|
||||
});
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
var typeClassFilter = require('dgeni-packages/ngdoc/rendering/filters/type-class');
|
||||
"use strict";
|
||||
|
||||
var encoder = new require('node-html-encoder').Encoder();
|
||||
|
||||
module.exports = {
|
||||
name: 'type',
|
||||
description: 'Replace with markup that displays a nice type',
|
||||
handlerFactory: function() {
|
||||
return function(doc, tagName, tagDescription) {
|
||||
return '<a href="" class="' + typeClassFilter.process(tagDescription) + '">'+encoder.htmlEncode(tagDescription) + '</a>';
|
||||
};
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @dgService typeInlineTagDef
|
||||
* @description
|
||||
* Replace with markup that displays a nice type
|
||||
*/
|
||||
module.exports = function typeInlineTagDef(getTypeClass) {
|
||||
return {
|
||||
name: 'type',
|
||||
handler: function(doc, tagName, tagDescription) {
|
||||
return '<a href="" class="' + getTypeClass(tagDescription) + '">'+encoder.htmlEncode(tagDescription) + '</a>';
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,33 +0,0 @@
|
||||
var fs = require('q-io/fs');
|
||||
var log = require('winston');
|
||||
var util = require("util");
|
||||
|
||||
module.exports = {
|
||||
name: 'debug-dump',
|
||||
runBefore: ['write-files'],
|
||||
description: 'This processor dumps docs that match a filter to a file',
|
||||
process: function(docs, config) {
|
||||
|
||||
var filter, outputPath, depth;
|
||||
|
||||
filter = config.get('processing.debug-dump.filter');
|
||||
outputPath = config.get('processing.debug-dump.outputPath');
|
||||
depth = config.get('processing.debug-dump.depth', 2);
|
||||
|
||||
|
||||
if ( filter && outputPath ) {
|
||||
log.info('Dumping docs:', filter, outputPath);
|
||||
var filteredDocs = filter(docs);
|
||||
var dumpedDocs = util.inspect(filteredDocs, depth);
|
||||
return writeFile(outputPath, dumpedDocs).then(function() {
|
||||
return docs;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function writeFile(file, content) {
|
||||
return fs.makeTree(fs.directory(file)).then(function() {
|
||||
return fs.write(file, content, 'wb');
|
||||
});
|
||||
}
|
||||
@@ -1,59 +1,52 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require('lodash');
|
||||
var log = require('winston');
|
||||
var path = require('canonical-path');
|
||||
|
||||
module.exports = {
|
||||
name: 'error-docs',
|
||||
description: 'Compute the various fields for docs in the Error area',
|
||||
runAfter: ['tags-extracted', 'compute-path'],
|
||||
runBefore: ['extra-docs-added'],
|
||||
exports: {
|
||||
errorNamespaces: ['factory', function() { return {}; }],
|
||||
minerrInfo: ['factory', function(config) {
|
||||
var minerrInfoPath = config.get('processing.errors.minerrInfoPath');
|
||||
if ( !minerrInfoPath ) {
|
||||
throw new Error('Error in configuration: Please provide a path to the minerr info file (errors.json) ' +
|
||||
'in the `config.processing.errors.minerrInfoPath` property');
|
||||
}
|
||||
return require(minerrInfoPath);
|
||||
}]
|
||||
},
|
||||
process: function(docs, partialNames, errorNamespaces, minerrInfo) {
|
||||
/**
|
||||
* @dgProcessor errorDocsProcessor
|
||||
* @description
|
||||
* Process "error" docType docs and generate errorNamespace docs
|
||||
*/
|
||||
module.exports = function errorDocsProcessor(errorNamespaceMap, getMinerrInfo) {
|
||||
return {
|
||||
$runAfter: ['tags-extracted'],
|
||||
$runBefore: ['extra-docs-added'],
|
||||
$process: function(docs) {
|
||||
|
||||
// Create error namespace docs and attach error docs to each
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( doc.docType === 'error' ) {
|
||||
// Create error namespace docs and attach error docs to each
|
||||
docs.forEach(function(doc) {
|
||||
var parts, namespaceDoc;
|
||||
|
||||
// Parse out the error info from the id
|
||||
parts = doc.name.split(':');
|
||||
doc.namespace = parts[0];
|
||||
doc.name = parts[1];
|
||||
if ( doc.docType === 'error' ) {
|
||||
|
||||
// Parse out the error info from the id
|
||||
parts = doc.name.split(':');
|
||||
doc.namespace = parts[0];
|
||||
doc.name = parts[1];
|
||||
|
||||
var namespaceDoc = errorNamespaces[doc.namespace];
|
||||
if ( !namespaceDoc ) {
|
||||
// First time we came across this namespace, so create a new one
|
||||
namespaceDoc = errorNamespaces[doc.namespace] = {
|
||||
area: doc.area,
|
||||
name: doc.namespace,
|
||||
errors: [],
|
||||
path: path.dirname(doc.path),
|
||||
outputPath: path.dirname(doc.outputPath) + '.html',
|
||||
docType: 'errorNamespace'
|
||||
};
|
||||
// Get or create the relevant errorNamespace doc
|
||||
namespaceDoc = errorNamespaceMap.get(doc.namespace);
|
||||
if ( !namespaceDoc ) {
|
||||
namespaceDoc = {
|
||||
area: 'error',
|
||||
name: doc.namespace,
|
||||
errors: [],
|
||||
docType: 'errorNamespace'
|
||||
};
|
||||
errorNamespaceMap.set(doc.namespace, namespaceDoc);
|
||||
}
|
||||
|
||||
// Link this error doc to its namespace doc
|
||||
namespaceDoc.errors.push(doc);
|
||||
doc.namespaceDoc = namespaceDoc;
|
||||
doc.formattedErrorMessage = getMinerrInfo().errors[doc.namespace][doc.name];
|
||||
}
|
||||
});
|
||||
|
||||
// Add this error to the namespace
|
||||
namespaceDoc.errors.push(doc);
|
||||
doc.namespace = namespaceDoc;
|
||||
|
||||
doc.formattedErrorMessage = minerrInfo.errors[doc.namespace.name][doc.name];
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
return docs.concat(_.values(errorNamespaces));
|
||||
}
|
||||
};
|
||||
errorNamespaceMap.forEach(function(errorNamespace) {
|
||||
docs.push(errorNamespace);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,20 +0,0 @@
|
||||
var gruntUtils = require('../../../lib/grunt/utils');
|
||||
var versionInfo = require('../../../lib/versions/version-info');
|
||||
|
||||
module.exports = {
|
||||
name: 'git-data',
|
||||
runBefore: ['reading-files'],
|
||||
description: 'This processor adds information from the local git repository to the extraData injectable',
|
||||
exports: {
|
||||
gitData: ['factory', function() {
|
||||
return {
|
||||
version: versionInfo.currentVersion,
|
||||
versions: versionInfo.previousVersions,
|
||||
info: versionInfo.gitRepoInfo
|
||||
};
|
||||
}]
|
||||
},
|
||||
process: function(extraData, gitData) {
|
||||
extraData.git = gitData;
|
||||
}
|
||||
};
|
||||
@@ -1,40 +1,43 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require('lodash');
|
||||
var log = require('winston');
|
||||
var path = require('canonical-path');
|
||||
|
||||
module.exports = {
|
||||
name: 'index-page',
|
||||
runAfter: ['adding-extra-docs'],
|
||||
runBefore: ['extra-docs-added'],
|
||||
description: 'This processor creates docs that will be rendered as the index page for the app',
|
||||
process: function(docs, config) {
|
||||
/**
|
||||
* @dgProcessor generateIndexPagesProcessor
|
||||
* @description
|
||||
* This processor creates docs that will be rendered as the index page for the app
|
||||
*/
|
||||
module.exports = function generateIndexPagesProcessor() {
|
||||
return {
|
||||
deployments: [],
|
||||
$validate: {
|
||||
deployments: { presence: true }
|
||||
},
|
||||
$runAfter: ['adding-extra-docs'],
|
||||
$runBefore: ['extra-docs-added'],
|
||||
$process: function(docs) {
|
||||
|
||||
var deployment = config.deployment;
|
||||
if ( !deployment || !deployment.environments ) {
|
||||
throw new Error('No deployment environments found in the config.');
|
||||
// Collect up all the areas in the docs
|
||||
var areas = {};
|
||||
docs.forEach(function(doc) {
|
||||
if ( doc.area ) {
|
||||
areas[doc.area] = doc.area;
|
||||
}
|
||||
});
|
||||
areas = _.keys(areas);
|
||||
|
||||
this.deployments.forEach(function(deployment) {
|
||||
|
||||
var indexDoc = _.defaults({
|
||||
docType: 'indexPage',
|
||||
areas: areas
|
||||
}, deployment);
|
||||
|
||||
indexDoc.id = 'index' + (deployment.name === 'default' ? '' : '-' + deployment.name);
|
||||
|
||||
docs.push(indexDoc);
|
||||
});
|
||||
}
|
||||
|
||||
// Collect up all the areas in the docs
|
||||
var areas = {};
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( doc.area ) {
|
||||
areas[doc.area] = doc.area;
|
||||
}
|
||||
});
|
||||
areas = _.keys(areas);
|
||||
|
||||
_.forEach(deployment.environments, function(environment) {
|
||||
|
||||
var indexDoc = _.defaults({
|
||||
docType: 'indexPage',
|
||||
areas: areas
|
||||
}, environment);
|
||||
|
||||
indexDoc.id = 'index' + (environment.name === 'default' ? '' : '-' + environment.name);
|
||||
// Use .. to put it at the root of the build
|
||||
indexDoc.outputPath = indexDoc.id + '.html';
|
||||
|
||||
docs.push(indexDoc);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -1,51 +1,64 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require('lodash');
|
||||
var log = require('winston');
|
||||
var fs = require('fs');
|
||||
var path = require('canonical-path');
|
||||
|
||||
module.exports = {
|
||||
name: 'keywords',
|
||||
runAfter: ['docs-processed', 'api-docs'],
|
||||
runBefore: ['adding-extra-docs'],
|
||||
description: 'This processor extracts all the keywords from the document',
|
||||
process: function(docs, config) {
|
||||
/**
|
||||
* @dgProcessor generateKeywordsProcessor
|
||||
* @description
|
||||
* This processor extracts all the keywords from each document and creates
|
||||
* a new document that will be rendered as a JavaScript file containing all
|
||||
* this data.
|
||||
*/
|
||||
module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
|
||||
return {
|
||||
ignoreWordsFile: undefined,
|
||||
areasToSearch: ['api', 'guide', 'misc', 'error', 'tutorial'],
|
||||
propertiesToIgnore: [],
|
||||
$validate: {
|
||||
ignoreWordsFile: { },
|
||||
areasToSearch: { presence: true },
|
||||
propertiesToIgnore: { }
|
||||
},
|
||||
$runAfter: ['memberDocsProcessor'],
|
||||
$runBefore: ['rendering-docs'],
|
||||
$process: function(docs) {
|
||||
|
||||
// Keywords to ignore
|
||||
var wordsToIgnore = [];
|
||||
var propertiesToIgnore;
|
||||
var areasToSearch;
|
||||
// Keywords to ignore
|
||||
var wordsToIgnore = [];
|
||||
var propertiesToIgnore;
|
||||
var areasToSearch;
|
||||
|
||||
// Keywords start with "ng:" or one of $, _ or a letter
|
||||
var KEYWORD_REGEX = /^((ng:|[\$_a-z])[\w\-_]+)/;
|
||||
// Keywords start with "ng:" or one of $, _ or a letter
|
||||
var KEYWORD_REGEX = /^((ng:|[\$_a-z])[\w\-_]+)/;
|
||||
|
||||
// Load up the keywords to ignore, if specified in the config
|
||||
if ( config.processing.search && config.processing.search.ignoreWordsFile ) {
|
||||
// Load up the keywords to ignore, if specified in the config
|
||||
if ( this.ignoreWordsFile ) {
|
||||
|
||||
var ignoreWordsPath = path.resolve(config.basePath, config.processing.search.ignoreWordsFile);
|
||||
wordsToIgnore = fs.readFileSync(ignoreWordsPath, 'utf8').toString().split(/[,\s\n\r]+/gm);
|
||||
var ignoreWordsPath = path.resolve(readFilesProcessor.basePath, this.ignoreWordsFile);
|
||||
wordsToIgnore = fs.readFileSync(ignoreWordsPath, 'utf8').toString().split(/[,\s\n\r]+/gm);
|
||||
|
||||
log.debug('Loaded ignore words from "' + ignoreWordsPath + '"');
|
||||
log.silly(wordsToIgnore);
|
||||
log.debug('Loaded ignore words from "' + ignoreWordsPath + '"');
|
||||
log.silly(wordsToIgnore);
|
||||
|
||||
}
|
||||
|
||||
areasToSearch = _.indexBy(config.get('processing.search.areasToSearch', ['api', 'guide', 'misc', 'error', 'tutorial']));
|
||||
|
||||
propertiesToIgnore = _.indexBy(config.get('processing.search.propertiesToIgnore', []));
|
||||
log.debug('Properties to ignore', propertiesToIgnore);
|
||||
|
||||
var ignoreWordsMap = _.indexBy(wordsToIgnore);
|
||||
|
||||
// If the title contains a name starting with ng, e.g. "ngController", then add the module name
|
||||
// without the ng to the title text, e.g. "controller".
|
||||
function extractTitleWords(title) {
|
||||
var match = /ng([A-Z]\w*)/.exec(title);
|
||||
if ( match ) {
|
||||
title = title + ' ' + match[1].toLowerCase();
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
areasToSearch = _.indexBy(this.areasToSearch);
|
||||
propertiesToIgnore = _.indexBy(this.propertiesToIgnore);
|
||||
log.debug('Properties to ignore', propertiesToIgnore);
|
||||
|
||||
var ignoreWordsMap = _.indexBy(wordsToIgnore);
|
||||
|
||||
// If the title contains a name starting with ng, e.g. "ngController", then add the module name
|
||||
// without the ng to the title text, e.g. "controller".
|
||||
function extractTitleWords(title) {
|
||||
var match = /ng([A-Z]\w*)/.exec(title);
|
||||
if ( match ) {
|
||||
title = title + ' ' + match[1].toLowerCase();
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
function extractWords(text, words, keywordMap) {
|
||||
|
||||
@@ -58,15 +71,15 @@ module.exports = {
|
||||
keywordMap[key] = true;
|
||||
words.push(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// We are only interested in docs that live in the right area
|
||||
docs = _.filter(docs, function(doc) { return areasToSearch[doc.area]; });
|
||||
// We are only interested in docs that live in the right area
|
||||
docs = _.filter(docs, function(doc) { return areasToSearch[doc.area]; });
|
||||
|
||||
_.forEach(docs, function(doc) {
|
||||
_.forEach(docs, function(doc) {
|
||||
|
||||
var words = [];
|
||||
var keywordMap = _.clone(ignoreWordsMap);
|
||||
@@ -94,7 +107,8 @@ module.exports = {
|
||||
members: _.sortBy(members).join(' ')
|
||||
};
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require('lodash');
|
||||
var path = require('canonical-path');
|
||||
var log = require('winston');
|
||||
|
||||
var AREA_NAMES = {
|
||||
api: 'API',
|
||||
@@ -33,189 +34,206 @@ function getNavGroup(pages, area, pageSorter, pageMapper) {
|
||||
}
|
||||
|
||||
|
||||
var navGroupMappers = {
|
||||
api: function(areaPages, area) {
|
||||
var navGroups = _(areaPages)
|
||||
.filter('module') // We are not interested in docs that are not in a module
|
||||
/**
|
||||
* @dgProcessor generatePagesDataProcessor
|
||||
* @description
|
||||
* This processor will create a new doc that will be rendered as a JavaScript file
|
||||
* containing meta information about the pages and navigation
|
||||
*/
|
||||
module.exports = function generatePagesDataProcessor(log) {
|
||||
|
||||
.groupBy('module')
|
||||
|
||||
.map(function(modulePages, moduleName) {
|
||||
log.debug('moduleName: ' + moduleName);
|
||||
var navItems = [];
|
||||
var modulePage;
|
||||
var navGroupMappers = {
|
||||
api: function(areaPages, area) {
|
||||
var navGroups = _(areaPages)
|
||||
.filter('module') // We are not interested in docs that are not in a module
|
||||
|
||||
_(modulePages)
|
||||
.groupBy('module')
|
||||
|
||||
.groupBy('docType')
|
||||
.map(function(modulePages, moduleName) {
|
||||
log.debug('moduleName: ' + moduleName);
|
||||
var navItems = [];
|
||||
var modulePage;
|
||||
|
||||
.tap(function(docTypes) {
|
||||
log.debug(_.keys(docTypes));
|
||||
// Extract the module page from the collection
|
||||
modulePage = docTypes.module[0];
|
||||
delete docTypes.module;
|
||||
})
|
||||
_(modulePages)
|
||||
|
||||
.tap(function(docTypes) {
|
||||
if ( docTypes.input ) {
|
||||
docTypes.directive = docTypes.directive || [];
|
||||
// Combine input docTypes into directive docTypes
|
||||
docTypes.directive = docTypes.directive.concat(docTypes.input);
|
||||
delete docTypes.input;
|
||||
}
|
||||
})
|
||||
.groupBy('docType')
|
||||
|
||||
.forEach(function(sectionPages, sectionName) {
|
||||
.tap(function(docTypes) {
|
||||
log.debug(moduleName, _.keys(docTypes));
|
||||
// Extract the module page from the collection
|
||||
modulePage = docTypes.module[0];
|
||||
delete docTypes.module;
|
||||
})
|
||||
|
||||
sectionPages = _.sortBy(sectionPages, 'name');
|
||||
.tap(function(docTypes) {
|
||||
if ( docTypes.input ) {
|
||||
docTypes.directive = docTypes.directive || [];
|
||||
// Combine input docTypes into directive docTypes
|
||||
docTypes.directive = docTypes.directive.concat(docTypes.input);
|
||||
delete docTypes.input;
|
||||
}
|
||||
})
|
||||
|
||||
if ( sectionPages.length > 0 ) {
|
||||
// Push a navItem for this section
|
||||
navItems.push({
|
||||
name: sectionName,
|
||||
type: 'section',
|
||||
href: path.dirname(sectionPages[0].path)
|
||||
});
|
||||
.forEach(function(sectionPages, sectionName) {
|
||||
|
||||
// Push the rest of the sectionPages for this section
|
||||
_.forEach(sectionPages, function(sectionPage) {
|
||||
sectionPages = _.sortBy(sectionPages, 'name');
|
||||
|
||||
if ( sectionPages.length > 0 ) {
|
||||
// Push a navItem for this section
|
||||
navItems.push({
|
||||
name: sectionPage.name,
|
||||
href: sectionPage.path,
|
||||
type: sectionPage.docType
|
||||
name: sectionName,
|
||||
type: 'section',
|
||||
href: path.dirname(sectionPages[0].path)
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
// Push the rest of the sectionPages for this section
|
||||
_.forEach(sectionPages, function(sectionPage) {
|
||||
|
||||
navItems.push({
|
||||
name: sectionPage.name,
|
||||
href: sectionPage.path,
|
||||
type: sectionPage.docType
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
return {
|
||||
name: moduleName,
|
||||
href: modulePage.path,
|
||||
type: 'group',
|
||||
navItems: navItems
|
||||
};
|
||||
})
|
||||
.value();
|
||||
return navGroups;
|
||||
},
|
||||
tutorial: function(pages, area) {
|
||||
return [getNavGroup(pages, area, 'step', function(page) {
|
||||
return {
|
||||
name: moduleName,
|
||||
href: modulePage.path,
|
||||
type: 'group',
|
||||
navItems: navItems
|
||||
name: page.name,
|
||||
step: page.step,
|
||||
href: page.path,
|
||||
type: 'tutorial'
|
||||
};
|
||||
})
|
||||
.value();
|
||||
return navGroups;
|
||||
},
|
||||
tutorial: function(pages, area) {
|
||||
return [getNavGroup(pages, area, 'step', function(page) {
|
||||
return {
|
||||
name: page.name,
|
||||
step: page.step,
|
||||
href: page.path,
|
||||
type: 'tutorial'
|
||||
};
|
||||
})];
|
||||
},
|
||||
error: function(pages, area) {
|
||||
return [getNavGroup(pages, area, 'path', function(page) {
|
||||
return {
|
||||
name: page.name,
|
||||
href: page.path,
|
||||
type: page.docType === 'errorNamespace' ? 'section' : 'error'
|
||||
};
|
||||
})];
|
||||
},
|
||||
pages: function(pages, area) {
|
||||
return [getNavGroup(
|
||||
pages,
|
||||
area,
|
||||
function(page) {
|
||||
return page.sortOrder || page.path;
|
||||
},
|
||||
function(page) {
|
||||
})];
|
||||
},
|
||||
error: function(pages, area) {
|
||||
return [getNavGroup(pages, area, 'path', function(page) {
|
||||
return {
|
||||
name: page.name,
|
||||
href: page.path,
|
||||
type: 'page'
|
||||
type: page.docType === 'errorNamespace' ? 'section' : 'error'
|
||||
};
|
||||
}
|
||||
)];
|
||||
}
|
||||
};
|
||||
})];
|
||||
},
|
||||
pages: function(pages, area) {
|
||||
return [getNavGroup(
|
||||
pages,
|
||||
area,
|
||||
function(page) {
|
||||
return page.sortOrder || page.path;
|
||||
},
|
||||
function(page) {
|
||||
return {
|
||||
name: page.name,
|
||||
href: page.path,
|
||||
type: 'page'
|
||||
};
|
||||
}
|
||||
)];
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
name: 'pages-data',
|
||||
description: 'This plugin will create a new doc that will be rendered as an angularjs module ' +
|
||||
'which will contain meta information about the pages and navigation',
|
||||
runAfter: ['adding-extra-docs', 'component-groups-generate', 'compute-path'],
|
||||
runBefore: ['extra-docs-added'],
|
||||
process: function(docs, config) {
|
||||
return {
|
||||
$runAfter: ['paths-computed', 'generateKeywordsProcessor'],
|
||||
$runBefore: ['rendering-docs'],
|
||||
$process: function(docs) {
|
||||
|
||||
var outputFolder = config.rendering.outputFolder;
|
||||
|
||||
_(docs)
|
||||
.filter(function(doc) { return doc.area === 'api'; })
|
||||
.filter(function(doc) { return doc.docType === 'module'; })
|
||||
.forEach(function(doc) { if ( !doc.path ) {
|
||||
log.warn('Missing path property for ', doc.id);
|
||||
}})
|
||||
.map(function(doc) { return _.pick(doc, ['id', 'module', 'docType', 'area']); })
|
||||
.tap(function(docs) {
|
||||
log.debug(docs);
|
||||
});
|
||||
|
||||
|
||||
// We are only interested in docs that are in a area and not landing pages
|
||||
var navPages = _.filter(docs, function(page) {
|
||||
return page.area &&
|
||||
page.docType != 'componentGroup';
|
||||
});
|
||||
|
||||
// Generate an object collection of pages that is grouped by area e.g.
|
||||
// - area "api"
|
||||
// - group "ng"
|
||||
// - section "directive"
|
||||
// - ngApp
|
||||
// - ngBind
|
||||
// - section "global"
|
||||
// - angular.element
|
||||
// - angular.bootstrap
|
||||
// - section "service"
|
||||
// - $compile
|
||||
// - group "ngRoute"
|
||||
// - section "directive"
|
||||
// - ngView
|
||||
// - section "service"
|
||||
// - $route
|
||||
//
|
||||
var areas = {};
|
||||
_(navPages)
|
||||
.groupBy('area')
|
||||
.forEach(function(pages, areaId) {
|
||||
var area = {
|
||||
id: areaId,
|
||||
name: AREA_NAMES[areaId]
|
||||
};
|
||||
areas[areaId] = area;
|
||||
|
||||
var navGroupMapper = navGroupMappers[area.id] || navGroupMappers['pages'];
|
||||
area.navGroups = navGroupMapper(pages, area);
|
||||
// We are only interested in docs that are in an area
|
||||
var pages = _.filter(docs, function(doc) {
|
||||
return doc.area;
|
||||
});
|
||||
|
||||
// Extract a list of basic page information for mapping paths to partials and for client side searching
|
||||
var pages = _(docs)
|
||||
.map(function(doc) {
|
||||
var page = _.pick(doc, [
|
||||
'docType', 'id', 'name', 'area', 'outputPath', 'path', 'searchTerms'
|
||||
]);
|
||||
return page;
|
||||
})
|
||||
.indexBy('path')
|
||||
.value();
|
||||
// We are only interested in pages that are not landing pages
|
||||
var navPages = _.filter(pages, function(page) {
|
||||
return page.docType != 'componentGroup';
|
||||
});
|
||||
|
||||
// Generate an object collection of pages that is grouped by area e.g.
|
||||
// - area "api"
|
||||
// - group "ng"
|
||||
// - section "directive"
|
||||
// - ngApp
|
||||
// - ngBind
|
||||
// - section "global"
|
||||
// - angular.element
|
||||
// - angular.bootstrap
|
||||
// - section "service"
|
||||
// - $compile
|
||||
// - group "ngRoute"
|
||||
// - section "directive"
|
||||
// - ngView
|
||||
// - section "service"
|
||||
// - $route
|
||||
//
|
||||
var areas = {};
|
||||
_(navPages)
|
||||
.groupBy('area')
|
||||
.forEach(function(pages, areaId) {
|
||||
var area = {
|
||||
id: areaId,
|
||||
name: AREA_NAMES[areaId]
|
||||
};
|
||||
areas[areaId] = area;
|
||||
|
||||
var navGroupMapper = navGroupMappers[area.id] || navGroupMappers['pages'];
|
||||
area.navGroups = navGroupMapper(pages, area);
|
||||
});
|
||||
|
||||
docs.push({
|
||||
docType: 'nav-data',
|
||||
id: 'nav-data',
|
||||
template: 'nav-data.template.js',
|
||||
outputPath: 'js/nav-data.js',
|
||||
areas: areas
|
||||
});
|
||||
|
||||
|
||||
var docData = {
|
||||
docType: 'pages-data',
|
||||
id: 'pages-data',
|
||||
template: 'pages-data.template.js',
|
||||
outputPath: 'js/pages-data.js',
|
||||
|
||||
areas: areas,
|
||||
pages: pages
|
||||
};
|
||||
docs.push(docData);
|
||||
}
|
||||
var searchData = _(pages)
|
||||
.filter(function(page) {
|
||||
return page.searchTerms;
|
||||
})
|
||||
.map(function(page) {
|
||||
return _.extend({ path: page.path }, page.searchTerms);
|
||||
})
|
||||
.value();
|
||||
|
||||
docs.push({
|
||||
docType: 'json-doc',
|
||||
id: 'search-data-json',
|
||||
template: 'json-doc.template.json',
|
||||
outputPath: 'js/search-data.json',
|
||||
data: searchData
|
||||
});
|
||||
|
||||
// Extract a list of basic page information for mapping paths to partials and for client side searching
|
||||
var pageData = _(docs)
|
||||
.map(function(doc) {
|
||||
return _.pick(doc, ['name', 'area', 'path']);
|
||||
})
|
||||
.indexBy('path')
|
||||
.value();
|
||||
|
||||
docs.push({
|
||||
docType: 'pages-data',
|
||||
id: 'pages-data',
|
||||
template: 'pages-data.template.js',
|
||||
outputPath: 'js/pages-data.js',
|
||||
pages: pageData
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
var _ = require('lodash');
|
||||
var path = require('canonical-path');
|
||||
|
||||
module.exports = {
|
||||
name: 'protractor-generate',
|
||||
description: 'Generate a protractor test file from the e2e tests in the examples',
|
||||
runAfter: ['adding-extra-docs'],
|
||||
runBefore: ['extra-docs-added'],
|
||||
process: function(docs, examples, config) {
|
||||
var protractorFolder = config.get('rendering.protractor.outputFolder', 'ptore2e');
|
||||
|
||||
_.forEach(examples, function(example) {
|
||||
|
||||
_.forEach(example.files, function(file) {
|
||||
|
||||
// Check if it's a Protractor test.
|
||||
if (file.type !== 'protractor') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new files for the tests.
|
||||
docs.push(createProtractorDoc(example, file, 'jquery'));
|
||||
docs.push(createProtractorDoc(example, file, 'jqlite'));
|
||||
});
|
||||
});
|
||||
|
||||
function createProtractorDoc(example, file, env) {
|
||||
var protractorDoc = {
|
||||
docType: 'e2e-test',
|
||||
id: 'protractorTest' + '-' + example.id,
|
||||
template: 'protractorTests.template.js',
|
||||
outputPath: path.join(protractorFolder, example.id, env + '_test.js'),
|
||||
innerTest: file.fileContents,
|
||||
pathPrefix: '.', // Hold for if we test with full jQuery
|
||||
exampleId: example.id,
|
||||
description: example.doc.id,
|
||||
'ng-app-included': example['ng-app-included']
|
||||
};
|
||||
|
||||
if (env === 'jquery') {
|
||||
protractorDoc.examplePath = example.outputFolder + '/index-jquery.html';
|
||||
} else {
|
||||
protractorDoc.examplePath = example.outputFolder + '/index.html';
|
||||
}
|
||||
return protractorDoc;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
@@ -1,38 +1,34 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require('lodash');
|
||||
|
||||
module.exports = {
|
||||
name: 'versions-data',
|
||||
description: 'This plugin will create a new doc that will be rendered as an angularjs module ' +
|
||||
'which will contain meta information about the versions of angular',
|
||||
runAfter: ['adding-extra-docs', 'pages-data'],
|
||||
runBefore: ['extra-docs-added'],
|
||||
process: function(docs, gitData) {
|
||||
/**
|
||||
* @dgProcessor generateVersionDocProcessor
|
||||
* @description
|
||||
* This processor will create a new doc that will be rendered as a JavaScript file
|
||||
* containing meta information about the current versions of AngularJS
|
||||
*/
|
||||
module.exports = function generateVersionDocProcessor(gitData) {
|
||||
return {
|
||||
$runAfter: ['generatePagesDataProcessor'],
|
||||
$runBefore: ['rendering-docs'],
|
||||
$process: function(docs) {
|
||||
|
||||
var version = gitData.version;
|
||||
var versions = gitData.versions;
|
||||
var versionDoc = {
|
||||
docType: 'versions-data',
|
||||
id: 'versions-data',
|
||||
template: 'versions-data.template.js',
|
||||
outputPath: 'js/versions-data.js',
|
||||
currentVersion: gitData.version
|
||||
};
|
||||
|
||||
if ( !version ) {
|
||||
throw new Error('Invalid configuration. Please provide a valid `source.currentVersion` property');
|
||||
versionDoc.versions = _(gitData.versions)
|
||||
.filter(function(version) { return version.major > 0; })
|
||||
.push(gitData.version)
|
||||
.reverse()
|
||||
.value();
|
||||
|
||||
docs.push(versionDoc);
|
||||
}
|
||||
if ( !versions ) {
|
||||
throw new Error('Invalid configuration. Please provide a valid `source.previousVersions` property');
|
||||
}
|
||||
|
||||
var versionDoc = {
|
||||
docType: 'versions-data',
|
||||
id: 'versions-data',
|
||||
template: 'versions-data.template.js',
|
||||
outputPath: 'js/versions-data.js',
|
||||
};
|
||||
|
||||
versionDoc.currentVersion = version;
|
||||
|
||||
versionDoc.versions = _(versions)
|
||||
.filter(function(version) { return version.major > 0; })
|
||||
.push(version)
|
||||
.reverse()
|
||||
.value();
|
||||
|
||||
docs.push(versionDoc);
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = function debugDeployment(getVersion) {
|
||||
return {
|
||||
name: 'debug',
|
||||
examples: {
|
||||
commonFiles: {
|
||||
scripts: [ '../../../angular.js' ]
|
||||
},
|
||||
dependencyPath: '../../../'
|
||||
},
|
||||
scripts: [
|
||||
'../angular.js',
|
||||
'../angular-resource.js',
|
||||
'../angular-route.js',
|
||||
'../angular-cookies.js',
|
||||
'../angular-sanitize.js',
|
||||
'../angular-touch.js',
|
||||
'../angular-animate.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/nav-data.js',
|
||||
'js/docs.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/docs.css',
|
||||
'css/animations.css'
|
||||
]
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = function defaultDeployment(getVersion) {
|
||||
return {
|
||||
name: 'default',
|
||||
examples: {
|
||||
commonFiles: {
|
||||
scripts: [ '../../../angular.min.js' ]
|
||||
},
|
||||
dependencyPath: '../../../'
|
||||
},
|
||||
scripts: [
|
||||
'../angular.min.js',
|
||||
'../angular-resource.min.js',
|
||||
'../angular-route.min.js',
|
||||
'../angular-cookies.min.js',
|
||||
'../angular-sanitize.min.js',
|
||||
'../angular-touch.min.js',
|
||||
'../angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/nav-data.js',
|
||||
'js/docs.min.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/docs.css',
|
||||
'css/animations.css'
|
||||
]
|
||||
};
|
||||
};
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = function jqueryDeployment(getVersion) {
|
||||
return {
|
||||
name: 'jquery',
|
||||
examples: {
|
||||
commonFiles: {
|
||||
scripts: [
|
||||
'../../components/jquery-' + getVersion('jquery') + '/jquery.js',
|
||||
'../../../angular.js'
|
||||
]
|
||||
},
|
||||
dependencyPath: '../../../'
|
||||
},
|
||||
scripts: [
|
||||
'components/jquery-' + getVersion('jquery') + '/jquery.js',
|
||||
'../angular.min.js',
|
||||
'../angular-resource.min.js',
|
||||
'../angular-route.min.js',
|
||||
'../angular-cookies.min.js',
|
||||
'../angular-sanitize.min.js',
|
||||
'../angular-touch.min.js',
|
||||
'../angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/nav-data.js',
|
||||
'js/docs.min.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/docs.css',
|
||||
'css/animations.css'
|
||||
]
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
"use strict";
|
||||
|
||||
var versionInfo = require('../../../../lib/versions/version-info');
|
||||
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + versionInfo.cdnVersion;
|
||||
|
||||
module.exports = function productionDeployment(getVersion) {
|
||||
return {
|
||||
name: 'production',
|
||||
examples: {
|
||||
commonFiles: {
|
||||
scripts: [ cdnUrl + '/angular.min.js' ]
|
||||
},
|
||||
dependencyPath: cdnUrl + '/'
|
||||
},
|
||||
scripts: [
|
||||
cdnUrl + '/angular.min.js',
|
||||
cdnUrl + '/angular-resource.min.js',
|
||||
cdnUrl + '/angular-route.min.js',
|
||||
cdnUrl + '/angular-cookies.min.js',
|
||||
cdnUrl + '/angular-sanitize.min.js',
|
||||
cdnUrl + '/angular-touch.min.js',
|
||||
cdnUrl + '/angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/nav-data.js',
|
||||
'js/docs.min.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/docs.css',
|
||||
'css/animations.css'
|
||||
]
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
var StringMap = require('stringmap');
|
||||
|
||||
/**
|
||||
* @dgService errorNamespaceMap
|
||||
* A map of error namespaces by name.
|
||||
*/
|
||||
module.exports = function errorNamespaceMap() {
|
||||
return new StringMap();
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
"use strict";
|
||||
|
||||
var path = require('canonical-path');
|
||||
|
||||
/**
|
||||
* @dgService minErrInfo
|
||||
* @description
|
||||
* Load the error information that was generated during the AngularJS build.
|
||||
*/
|
||||
module.exports = function getMinerrInfo(readFilesProcessor) {
|
||||
return function() {
|
||||
var minerrInfoPath = path.resolve(readFilesProcessor.basePath, 'build/errors.json');
|
||||
return require(minerrInfoPath);
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
var path = require('canonical-path');
|
||||
|
||||
/**
|
||||
* dgService getVersion
|
||||
* @description
|
||||
* Find the current version of the bower component (or npm module)
|
||||
*/
|
||||
module.exports = function getVersion(readFilesProcessor) {
|
||||
var basePath = readFilesProcessor.basePath;
|
||||
|
||||
return function(component, sourceFolder, packageFile) {
|
||||
sourceFolder = path.resolve(basePath, sourceFolder || 'docs/bower_components');
|
||||
packageFile = packageFile || 'bower.json';
|
||||
return require(path.join(sourceFolder,component,packageFile)).version;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,17 @@
|
||||
"use strict";
|
||||
|
||||
var gruntUtils = require('../../../lib/grunt/utils');
|
||||
var versionInfo = require('../../../lib/versions/version-info');
|
||||
|
||||
/**
|
||||
* @dgService gitData
|
||||
* @description
|
||||
* Information from the local git repository
|
||||
*/
|
||||
module.exports = function gitData() {
|
||||
return {
|
||||
version: versionInfo.currentVersion,
|
||||
versions: versionInfo.previousVersions,
|
||||
info: versionInfo.gitRepoInfo
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html lang="en" ng-app="docsApp" ng-controller="DocsController">
|
||||
<html lang="en" ng-app="docsApp" ng-strict-di ng-controller="DocsController">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta charset="utf-8">
|
||||
@@ -56,15 +56,6 @@
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
// force page reload when new update is available
|
||||
window.applicationCache && window.applicationCache.addEventListener('updateready', function(e) {
|
||||
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
|
||||
window.applicationCache.swapCache();
|
||||
window.location.reload();
|
||||
}
|
||||
}, false);
|
||||
|
||||
// GA asynchronous tracker
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-8594346-3']);
|
||||
@@ -85,7 +76,7 @@
|
||||
<div class="row">
|
||||
<div class="col-md-9 header-branding">
|
||||
<a class="brand navbar-brand" href="http://angularjs.org">
|
||||
<img class="logo" src="img/angularjs-for-header-only.svg">
|
||||
<img width="117" height="30" class="logo" ng-src="img/angularjs-for-header-only.svg">
|
||||
</a>
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="divider-vertical"></li>
|
||||
@@ -219,7 +210,7 @@
|
||||
</div>
|
||||
<div class="grid-right">
|
||||
<div id="loading" ng-show="loading">Loading...</div>
|
||||
<div ng-hide="loading" ng-include="currentPage.outputPath || 'Error404.html'" onload="afterPartialLoaded()" autoscroll></div>
|
||||
<div ng-hide="loading" ng-include="partialPath" autoscroll></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{$ doc.data | json $}
|
||||
@@ -0,0 +1,3 @@
|
||||
// Meta data used by the AngularJS docs app
|
||||
angular.module('navData', [])
|
||||
.value('NG_NAVIGATION', {$ doc.areas | json $});
|
||||
@@ -1,4 +1,3 @@
|
||||
// Meta data used by the AngularJS docs app
|
||||
angular.module('pagesData', [])
|
||||
.value('NG_PAGES', {$ doc.pages | json $})
|
||||
.value('NG_NAVIGATION', {$ doc.areas | json $});
|
||||
.value('NG_PAGES', {$ doc.pages | json $});
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
describe("{$ doc.description $}", function() {
|
||||
var rootEl;
|
||||
beforeEach(function() {
|
||||
rootEl = browser.rootEl;{% if doc['ng-app-included'] %}
|
||||
browser.rootEl = '[ng-app]';{% endif %}
|
||||
browser.get("{$ doc.pathPrefix $}/{$ doc.examplePath $}");
|
||||
});
|
||||
{% if doc['ng-app-included'] %}afterEach(function() { browser.rootEl = rootEl; });{% endif %}
|
||||
{$ doc.innerTest $}
|
||||
});
|
||||
@@ -2,15 +2,16 @@
|
||||
is HTML and wrap each line in a <p> - thus breaking the HTML #}
|
||||
|
||||
<div>
|
||||
<a ng-click="openPlunkr('{$ doc.example.outputFolder $}')" class="btn pull-right">
|
||||
<a ng-click="openPlunkr('{$ doc.path $}')" class="btn pull-right">
|
||||
<i class="glyphicon glyphicon-edit"> </i>
|
||||
Edit in Plunker</a>
|
||||
|
||||
<div class="runnable-example"
|
||||
path="{$ doc.example.outputFolder $}"
|
||||
path="{$ doc.example.deployments.default.path $}"
|
||||
{%- for attrName, attrValue in doc.example.attributes %}
|
||||
{$ attrName $}="{$ attrValue $}"{% endfor %}>
|
||||
|
||||
{% for fileName, file in doc.example.files %}
|
||||
{% for fileName, file in doc.example.files %}
|
||||
<div class="runnable-example-file" {% for attrName, attrValue in file.attributes %}
|
||||
{$ attrName $}="{$ attrValue $}"{% endfor %}>
|
||||
{% code -%}
|
||||
@@ -19,7 +20,7 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<iframe class="runnable-example-frame" src="{$ doc.example.outputFolder $}/index.html" name="{$ doc.example.id $}"></iframe>
|
||||
<iframe class="runnable-example-frame" src="{$ doc.example.deployments.default.outputPath $}" name="{$ doc.example.id $}"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ The documentation is organized into **{@link guide/module modules}** which conta
|
||||
These components are {@link guide/directive directives}, {@link guide/services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates templates}, global APIs, and testing mocks.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Angular Namespaces `$` and `$$`**
|
||||
**Angular Prefixes `$` and `$$`**:
|
||||
|
||||
To prevent accidental name collisions with your code,
|
||||
Angular prefixes names of public objects with `$` and names of private objects with `$$`.
|
||||
Please do not use the `$` or `$$` prefix in your code.
|
||||
</div>
|
||||
|
||||
## Angular Namespace
|
||||
## Angular Modules
|
||||
|
||||
|
||||
## {@link ng ng (core module)}
|
||||
|
||||
@@ -12,10 +12,10 @@ $controller(MyController);
|
||||
$controller(MyController, {scope: newScope});
|
||||
```
|
||||
|
||||
To fix the example above please provide a scope to the $controller call:
|
||||
To fix the example above please provide a scope (using the `$scope` property in the locals object) to the $controller call:
|
||||
|
||||
```
|
||||
$controller(MyController, {$scope, newScope});
|
||||
$controller(MyController, {$scope: newScope});
|
||||
```
|
||||
|
||||
Please consult the {@link ng.$controller $controller} service api docs to learn more.
|
||||
|
||||
@@ -54,4 +54,18 @@ angular.module('myModule')
|
||||
.directive('myDirective', ['myCoolService', function (myCoolService) {
|
||||
// This directive definition does not throw unknown provider.
|
||||
}]);
|
||||
```
|
||||
```
|
||||
|
||||
|
||||
Attempting to inject one controller into another will also throw an `Unknown provider` error:
|
||||
|
||||
```
|
||||
angular.module('myModule', [])
|
||||
.controller('MyFirstController', function() { /* ... */ });
|
||||
.controller('MySecondController', ['MyFirstController', function(MyFirstController) {
|
||||
// This controller throws an unknown provider error because
|
||||
// MyFirstController cannot be injected.
|
||||
}]);
|
||||
```
|
||||
|
||||
Use the `$controller` service if you want to instantiate controllers yourself.
|
||||
|
||||
@@ -4,7 +4,19 @@
|
||||
@description
|
||||
|
||||
If you configure {@link ng.$location `$location`} to use
|
||||
{@link api/ng.provider.$locationProvider `html5Mode`} (`history.pushState`), you need to specify the base URL for the application with a [`<base href="">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag.
|
||||
{@link api/ng.provider.$locationProvider `html5Mode`} (`history.pushState`), you need to specify the base URL for the application with a [`<base href="">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base) tag or configure
|
||||
`$locationProvider` to not require a base tag by passing a definition object with
|
||||
`requireBase:false` to `$locationProvider.html5Mode()`:
|
||||
|
||||
```javascript
|
||||
$locationProvider.html5Mode({
|
||||
enabled: true,
|
||||
requireBase: false
|
||||
});
|
||||
```
|
||||
|
||||
Note that removing the requirement for a <base> tag will have adverse side effects when resolving
|
||||
relative paths with `$location` in IE9.
|
||||
|
||||
The base URL is then used to resolve all relative URLs throughout the application regardless of the
|
||||
entry point into the app.
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
@ngdoc error
|
||||
@name ngModel:datefmt
|
||||
@fullName Model is not a date object
|
||||
@description
|
||||
|
||||
All date-related inputs like `<input type="date">` require the model to be a `Date` object.
|
||||
If the model is something else, this error will be thrown.
|
||||
Angular does not set validation errors on the `<input>` in this case
|
||||
as those errors are shown to the user, but the erroneous state was
|
||||
caused by incorrect application logic and not by the user.
|
||||
|
||||
@@ -91,10 +91,11 @@ To configure the `$location` service, retrieve the
|
||||
{@link ng.$locationProvider $locationProvider} and set the parameters as follows:
|
||||
|
||||
|
||||
- **html5Mode(mode)**: {boolean}<br />
|
||||
`true` - see HTML5 mode<br />
|
||||
`false` - see Hashbang mode<br />
|
||||
default: `false`
|
||||
- **html5Mode(mode)**: {boolean|Object}<br />
|
||||
`true` or `enabled:true` - see HTML5 mode<br />
|
||||
`false` or `enabled:false` - see Hashbang mode<br />
|
||||
`requireBase:true` - see Relative links<br />
|
||||
default: `enabled:false`
|
||||
|
||||
- **hashPrefix(prefix)**: {string}<br />
|
||||
prefix used for Hashbang URLs (used in Hashbang mode or in legacy browser in Html5 mode)<br />
|
||||
@@ -164,7 +165,7 @@ encoded.
|
||||
|
||||
`$location` service has two configuration modes which control the format of the URL in the browser
|
||||
address bar: **Hashbang mode** (the default) and the **HTML5 mode** which is based on using the
|
||||
HTML5 [History API](http://www.w3.org/TR/html5/history.html). Applications use the same API in
|
||||
HTML5 [History API](http://www.w3.org/TR/html5/introduction.html#history-0). Applications use the same API in
|
||||
both modes and the `$location` service will work with appropriate URL segments and browser APIs to
|
||||
facilitate the browser URL change and history management.
|
||||
|
||||
@@ -245,7 +246,7 @@ it('should show example', inject(
|
||||
## HTML5 mode
|
||||
|
||||
In HTML5 mode, the `$location` service getters and setters interact with the browser URL address
|
||||
through the HTML5 history API, which allows for use of regular URL path and search segments,
|
||||
through the HTML5 history API. This allows for use of regular URL path and search segments,
|
||||
instead of their hashbang equivalents. If the HTML5 History API is not supported by a browser, the
|
||||
`$location` service will fall back to using the hashbang URLs automatically. This frees you from
|
||||
having to worry about whether the browser displaying your app supports the history API or not; the
|
||||
@@ -328,9 +329,11 @@ reload to the original link.
|
||||
|
||||
### Relative links
|
||||
|
||||
Be sure to check all relative links, images, scripts etc. Angular requires you to specify the url base in
|
||||
the head of your main html file (`<base href="/my-base">`). With that, relative urls will
|
||||
always be resolved to this base url, event if the initial url of the document was different.
|
||||
Be sure to check all relative links, images, scripts etc. Angular requires you to specify the url
|
||||
base in the head of your main html file (`<base href="/my-base">`) unless `html5Mode.requireBase` is
|
||||
set to `false` in the html5Mode definition object passed to `$locationProvider.html5Mode()`. With
|
||||
that, relative urls will always be resolved to this base url, event if the initial url of the
|
||||
document was different.
|
||||
|
||||
There is one exception: Links that only contain a hash fragment (e.g. `<a href="#target">`)
|
||||
will only change `$location.hash()` and not modify the url otherwise. This is useful for scrolling
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
@ngdoc overview
|
||||
@name Accessibility
|
||||
@description
|
||||
|
||||
|
||||
# Accessibility with ngAria
|
||||
|
||||
You can use the `ngAria` module to have certain ARIA attributes automatically applied when you
|
||||
use certain directives.
|
||||
|
||||
```js
|
||||
angular.module('myApp', ['ngAria'])...
|
||||
```
|
||||
|
||||
Elements using `ng-model` with `required` or `ngRequired` directives will automatically have
|
||||
`aria-required` attributes with the proper corresponding values.
|
||||
|
||||
```html
|
||||
<material-input ng-model="val" required>
|
||||
```
|
||||
|
||||
Becomes:
|
||||
|
||||
```html
|
||||
<material-input ng-model="val" required aria-required="true">
|
||||
```
|
||||
|
||||
ngAria is just a starting point. You'll have to manually choose how to implement some
|
||||
accessibility features.
|
||||
|
||||
For instance, you may want to add `ng-keypress` bindings alongside `ng-click` to make keyboard
|
||||
navigation easier.
|
||||
|
||||
|
||||
## Additional Resources
|
||||
|
||||
Accessibility best practices that apply to web apps in general also apply to Angular.
|
||||
|
||||
* [WebAim](http://webaim.org/)
|
||||
* [Using WAI-ARIA in HTML](http://www.w3.org/TR/2014/WD-aria-in-html-20140626/)
|
||||
@@ -87,7 +87,9 @@ Here is an example of manually initializing Angular:
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
Hello {{greetMe}}!
|
||||
<div ng-controller="MyController">
|
||||
Hello {{greetMe}}!
|
||||
</div>
|
||||
<script src="http://code.angularjs.org/snapshot/angular.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -25,8 +25,8 @@ browser how the window size needs to be divided in half so that the center is fo
|
||||
center needs to be aligned with the text's center. Simply add an `align="center"` attribute to any
|
||||
element to achieve the desired behavior. Such is the power of declarative language.
|
||||
|
||||
But the declarative language is also limited, since it does not allow you to teach the browser new
|
||||
syntax. For example there is no easy way to get the browser to align the text at 1/3 the position
|
||||
However, the declarative language is also limited, as it does not allow you to teach the browser new
|
||||
syntax. For example, there is no easy way to get the browser to align the text at 1/3 the position
|
||||
instead of 1/2. What is needed is a way to teach the browser new HTML syntax.
|
||||
|
||||
Angular comes pre-bundled with common directives which are useful for building any app. We also
|
||||
@@ -85,7 +85,9 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
|
||||
position: 'relative',
|
||||
border: '1px solid red',
|
||||
backgroundColor: 'lightgrey',
|
||||
cursor: 'pointer'
|
||||
cursor: 'pointer',
|
||||
display: 'block',
|
||||
width: '65px'
|
||||
});
|
||||
element.on('mousedown', function(event) {
|
||||
// Prevent default dragging of selected content
|
||||
|
||||
@@ -67,8 +67,8 @@ element that adds extra behavior to the element. The {@link ng.directive:ngModel
|
||||
stores/updates the value of the input field into/from a variable.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Custom directives to access the DOM**: In Angular, the only place where an application touches the DOM is
|
||||
within directives. This is good as artifacts that access the DOM are hard to test.
|
||||
**Custom directives to access the DOM**: In Angular, the only place where an application should access the DOM is
|
||||
within directives. This is important because artifacts that access the DOM are hard to test.
|
||||
If you need to access the DOM directly you should write a custom directive for this. The
|
||||
{@link directive directives guide} explains how to do this.
|
||||
</div>
|
||||
|
||||
@@ -22,7 +22,7 @@ At a high level, directives are markers on a DOM element (such as an attribute,
|
||||
name, comment or CSS class) that tell AngularJS's **HTML compiler** ({@link ng.$compile `$compile`}) to
|
||||
attach a specified behavior to that DOM element or even transform the DOM element and its children.
|
||||
|
||||
Angular comes with a set of these directives built-in, like `ngBind`, `ngModel`, and `ngView`.
|
||||
Angular comes with a set of these directives built-in, like `ngBind`, `ngModel`, and `ngClass`.
|
||||
Much like you create controllers and services, you can create your own directives for Angular to use.
|
||||
When Angular {@link guide/bootstrap bootstraps} your application, the
|
||||
{@link guide/compiler HTML compiler} traverses the DOM matching directives against the DOM elements.
|
||||
@@ -160,7 +160,7 @@ restrictions, you cannot simply write `cx="{{cx}}"`.
|
||||
With `ng-attr-cx` you can work around this problem.
|
||||
|
||||
If an attribute with a binding is prefixed with the `ngAttr` prefix (denormalized as `ng-attr-`)
|
||||
then during the binding will be applied to the corresponding unprefixed attribute. This allows
|
||||
then during the binding it will be applied to the corresponding unprefixed attribute. This allows
|
||||
you to bind to attributes that would otherwise be eagerly processed by browsers
|
||||
(e.g. an SVG element's `circle[cx]` attributes). When using `ngAttr`, the `allOrNothing` flag of
|
||||
{@link ng.$interpolate $interpolate} is used, so if any expression in the interpolated string
|
||||
@@ -282,9 +282,6 @@ using `templateUrl` instead:
|
||||
</file>
|
||||
</example>
|
||||
|
||||
Great! But what if we wanted to have our directive match the tag name `<my-customer>` instead?
|
||||
If we simply put a `<my-customer>` element into the HTML, it doesn't work.
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** When you create a directive, it is restricted to attribute and elements only by default. In order to
|
||||
create directives that are triggered by class name, you need to use the `restrict` option.
|
||||
|
||||
@@ -87,12 +87,16 @@ This factory function should return a new filter function which takes the input
|
||||
as the first argument. Any filter arguments are passed in as additional arguments to the filter
|
||||
function.
|
||||
|
||||
The filter function should be a [pure function](http://en.wikipedia.org/wiki/Pure_function), which
|
||||
means that it should be stateless and idempotent. Angular relies on these properties and executes
|
||||
the filter only when the inputs to the function change.
|
||||
|
||||
The following sample filter reverses a text string. In addition, it conditionally makes the
|
||||
text upper-case.
|
||||
|
||||
<example module="myReverseModule">
|
||||
<example module="myReverseFilterApp">
|
||||
<file name="index.html">
|
||||
<div ng-controller="Controller">
|
||||
<div ng-controller="MyController">
|
||||
<input ng-model="greeting" type="text"><br>
|
||||
No filter: {{greeting}}<br>
|
||||
Reverse: {{greeting|reverse}}<br>
|
||||
@@ -101,7 +105,7 @@ text upper-case.
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
angular.module('myReverseModule', [])
|
||||
angular.module('myReverseFilterApp', [])
|
||||
.filter('reverse', function() {
|
||||
return function(input, uppercase) {
|
||||
input = input || '';
|
||||
@@ -116,12 +120,53 @@ text upper-case.
|
||||
return out;
|
||||
};
|
||||
})
|
||||
.controller('Controller', ['$scope', function($scope) {
|
||||
.controller('MyController', ['$scope', function($scope) {
|
||||
$scope.greeting = 'hello';
|
||||
}]);
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
## Stateful filters
|
||||
|
||||
It is strongly discouraged to write filters that are stateful, because the execution of those can't
|
||||
be optimized by Angular, which often leads to performance issues. Many stateful filters can be
|
||||
converted into stateless filters just by exposing the hidden state as a model and turning it into an
|
||||
argument for the filter.
|
||||
|
||||
If you however do need to write a stateful filter, you have to mark the filter as `$stateful`, which
|
||||
means that it will be executed one or more times during the each `$digest` cycle.
|
||||
|
||||
<example module="myStatefulFilterApp">
|
||||
<file name="index.html">
|
||||
<div ng-controller="MyController">
|
||||
Input: <input ng-model="greeting" type="text"><br>
|
||||
Decoration: <input ng-model="decoration.symbol" type="text"><br>
|
||||
No filter: {{greeting}}<br>
|
||||
Decorated: {{greeting | decorate}}<br>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
angular.module('myStatefulFilterApp', [])
|
||||
.filter('decorate', ['decoration', function(decoration) {
|
||||
|
||||
function decorateFilter(input) {
|
||||
return decoration.symbol + input + decoration.symbol;
|
||||
}
|
||||
decorateFilter.$stateful = true;
|
||||
|
||||
return decorateFilter;
|
||||
}])
|
||||
.controller('MyController', ['$scope', 'decoration', function($scope, decoration) {
|
||||
$scope.greeting = 'hello';
|
||||
$scope.decoration = decoration;
|
||||
}])
|
||||
.value('decoration', {symbol: '*'});
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
## Testing custom filters
|
||||
|
||||
See the [phonecat tutorial](http://docs.angularjs.org/tutorial/step_09#test) for an example.
|
||||
|
||||
@@ -213,6 +213,7 @@ only when the control loses focus (blur event).
|
||||
<input type="text" ng-model="user.data" /><br />
|
||||
</form>
|
||||
<pre>username = "{{user.name}}"</pre>
|
||||
<pre>userdata = "{{user.data}}"</pre>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
@@ -286,7 +287,7 @@ In the following example we create two directives.
|
||||
* The first one is `integer` and it validates whether the input is a valid integer.
|
||||
For example `1.23` is an invalid value, since it contains a fraction.
|
||||
Note that we unshift the array instead of pushing.
|
||||
This is because we want to be first parser and consume the control string value, as we need to execute the validation function before a conversion to number occurs.
|
||||
This is because we want it to be the first parser and consume the control string value, as we need to execute the validation function before a conversion to number occurs.
|
||||
|
||||
* The second directive is a `smart-float`.
|
||||
It parses both `1.2` and `1,2` into a valid float number `1.2`.
|
||||
|
||||
@@ -70,7 +70,7 @@ In Angular applications, you move the job of filling page templates with data fr
|
||||
|
||||
This is a short list of libraries with specific support and documentation for working with Angular. You can find a full list of all known Angular external libraries at [ngmodules.org](http://ngmodules.org/).
|
||||
|
||||
* **Internationalization:** [angular-translate](http://angular-translate.github.io), [angular-gettext](http://angular-gettext.rocketeer.be/)
|
||||
* **Internationalization:** [angular-translate](http://angular-translate.github.io), [angular-gettext](http://angular-gettext.rocketeer.be/), [angular-localization](http://doshprompt.github.io/angular-localization/)
|
||||
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
|
||||
* **SQL and NoSQL backends:** [BreezeJS](http://www.breezejs.com/), [AngularFire](http://angularfire.com/)
|
||||
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/), [ngTagsInput](https://github.com/mbenford/ngTagsInput)
|
||||
@@ -105,6 +105,7 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
* [Recipes With AngularJS](http://www.amazon.co.uk/Recipes-Angular-js-Frederik-Dietz-ebook/dp/B00DK95V48) by Frederik Dietz
|
||||
* [Developing an AngularJS Edge](http://www.amazon.com/Developing-AngularJS-Edge-Christopher-Hiller-ebook/dp/B00CJLFF8K) by Christopher Hiller
|
||||
* [ng-book: The Complete Book on AngularJS](http://ng-book.com/) by Ari Lerner
|
||||
* [AngularJS : Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
|
||||
|
||||
###Videos:
|
||||
* [egghead.io](http://egghead.io/)
|
||||
|
||||
@@ -96,7 +96,7 @@ this limitation, use a regular expression object as the value for the expression
|
||||
//after
|
||||
$scope.exp = /abc/i;
|
||||
- **Scope:** due to [8c6a8171](https://github.com/angular/angular.js/commit/8c6a8171f9bdaa5cdabc0cc3f7d3ce10af7b434d),
|
||||
Scope#$id is now of time number rather than string. Since the
|
||||
Scope#$id is now of type number rather than string. Since the
|
||||
id is primarily being used for debugging purposes this change should not affect
|
||||
anyone.
|
||||
- **forEach:** due to [55991e33](https://github.com/angular/angular.js/commit/55991e33af6fece07ea347a059da061b76fc95f5),
|
||||
|
||||
@@ -409,7 +409,15 @@ To wrap it up, let's summarize the most important points:
|
||||
<td class="success">yes\*\*</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>can create functions/primitives</td>
|
||||
<td>can create functions</td>
|
||||
<td class="success">yes</td>
|
||||
<td class="success">yes</td>
|
||||
<td class="success">yes</td>
|
||||
<td class="success">yes</td>
|
||||
<td class="success">yes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>can create primitives</td>
|
||||
<td class="success">yes</td>
|
||||
<td class="error">no</td>
|
||||
<td class="success">yes</td>
|
||||
|
||||
@@ -338,6 +338,16 @@ We inject the $compile service and $rootScope before each jasmine test. The $com
|
||||
to render the aGreatEye directive. After rendering the directive we ensure that the directive has
|
||||
replaced the content and "lidless, wreathed in flame, 2 times" is present.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Underscore notation**:
|
||||
|
||||
The use of the underscore notation (e.g.: `_$rootScope_`) is a convention wide spread in AngularJS
|
||||
community to keep the variable names clean in your tests. That's why the
|
||||
{@link $injector} strips out the leading and the trailing underscores when
|
||||
matching the parameters. The underscore rule applies ***only*** if the name starts **and** ends with
|
||||
exactly one underscore, otherwise no replacing happens.
|
||||
</div>
|
||||
|
||||
### Testing Transclusion Directives
|
||||
|
||||
Directives that use transclusion are treated specially by the compiler. Before their compile
|
||||
|
||||
@@ -205,7 +205,7 @@ If you want to apply a directive to each inner piece of the repeat, put it on a
|
||||
|
||||
### `$rootScope` exists, but it can be used for evil
|
||||
|
||||
Scopes in Angular form a hierarchy, prototypically inheriting from a root scope at the top of the tree.
|
||||
Scopes in Angular form a hierarchy, prototypally inheriting from a root scope at the top of the tree.
|
||||
Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.
|
||||
|
||||
Occasionally there are pieces of data that you want to make global to the whole app.
|
||||
|
||||
@@ -233,7 +233,7 @@ browser is limited, which results in your karma tests running extremely slow.
|
||||
|
||||
Refresh your browser and verify that it says "Hello, World!".
|
||||
|
||||
* Update the unit test for the controller in ./test/unit/controllersSpec.js to reflect the previous change. For example by adding:
|
||||
* Update the unit test for the controller in `./test/unit/controllersSpec.js` to reflect the previous change. For example by adding:
|
||||
|
||||
expect(scope.name).toBe('World');
|
||||
|
||||
@@ -251,7 +251,7 @@ browser is limited, which results in your karma tests running extremely slow.
|
||||
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
|
||||
</table>
|
||||
|
||||
Extra points: try and make an 8x8 table using an additional ng-repeat.
|
||||
Extra points: try and make an 8x8 table using an additional `ng-repeat`.
|
||||
|
||||
* Make the unit test fail by changing `expect(scope.phones.length).toBe(3)` to instead use `toBe(4)`.
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ describe('PhoneCat App', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should filter the phone list as user types into the search box', function() {
|
||||
it('should filter the phone list as a user types into the search box', function() {
|
||||
|
||||
var phoneList = element.all(by.repeater('phone in phones'));
|
||||
var query = element(by.model('query'));
|
||||
@@ -159,7 +159,7 @@ Let's see how we can get the current value of the `query` model to appear in the
|
||||
var phoneList = element.all(by.repeater('phone in phones'));
|
||||
var query = element(by.model('query'));
|
||||
|
||||
it('should filter the phone list as user types into the search box', function() {
|
||||
it('should filter the phone list as a user types into the search box', function() {
|
||||
expect(phoneList.count()).toBe(3);
|
||||
|
||||
query.sendKeys('nexus');
|
||||
|
||||
@@ -11,7 +11,7 @@ from our server using one of Angular's built-in {@link guide/services services}
|
||||
ng.$http $http}. We will use Angular's {@link guide/di dependency
|
||||
injection (DI)} to provide the service to the `PhoneListCtrl` controller.
|
||||
|
||||
* There are now a list of 20 phones, loaded from the server.
|
||||
* There is now a list of 20 phones, loaded from the server.
|
||||
|
||||
<div doc-tutorial-reset="5"></div>
|
||||
|
||||
|
||||
@@ -26,15 +26,16 @@ We are using [Bower][bower] to install client side dependencies. This step upda
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "angular-seed",
|
||||
"name": "angular-phonecat",
|
||||
"description": "A starter project for AngularJS",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/angular/angular-seed",
|
||||
"homepage": "https://github.com/angular/angular-phonecat",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"angular": "1.2.x",
|
||||
"angular-mocks": "~1.2.x",
|
||||
"jquery": "1.10.2",
|
||||
"bootstrap": "~3.1.1",
|
||||
"angular-route": "~1.2.x"
|
||||
}
|
||||
|
||||
@@ -49,8 +49,15 @@ and install this dependency. We can do this by running:
|
||||
npm install
|
||||
```
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Warning:** If a new version of Angular has been released since you last ran `npm install`, then you may have a
|
||||
problem with the `bower install` due to a conflict between the versions of angular.js that need to
|
||||
be installed. If you get this then simply delete your `app/bower_components` folder before running
|
||||
`npm install`.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
If you have bower installed globally then you can run `bower install` but for this project we have
|
||||
**Note:** If you have bower installed globally then you can run `bower install` but for this project we have
|
||||
preconfigured `npm install` to run bower for us.
|
||||
</div>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ animations on top of the template code we created before.
|
||||
## Dependencies
|
||||
|
||||
The animation functionality is provided by Angular in the `ngAnimate` module, which is distributed
|
||||
separately from the core Angular framework. In addition we will use `JQuery` in this project to do
|
||||
separately from the core Angular framework. In addition we will use `jQuery` in this project to do
|
||||
extra JavaScript animations.
|
||||
|
||||
We are using [Bower][bower] to install client side dependencies. This step updates the
|
||||
@@ -49,8 +49,8 @@ We are using [Bower][bower] to install client side dependencies. This step upda
|
||||
|
||||
* `"angular-animate": "~1.2.x"` tells bower to install a version of the
|
||||
angular-animate component that is compatible with version 1.2.x.
|
||||
* `"jquery": "1.10.2"` tells bower to install the 1.10.2 version of JQuery. Note that this is not an
|
||||
Angular library, it is the standard JQuery library. We can use bower to install a wide range of 3rd
|
||||
* `"jquery": "1.10.2"` tells bower to install the 1.10.2 version of jQuery. Note that this is not an
|
||||
Angular library, it is the standard jQuery library. We can use bower to install a wide range of 3rd
|
||||
party libraries.
|
||||
|
||||
We must ask bower to download and install this dependency. We can do this by running:
|
||||
@@ -59,8 +59,15 @@ We must ask bower to download and install this dependency. We can do this by run
|
||||
npm install
|
||||
```
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Warning:** If a new version of Angular has been released since you last ran `npm install`, then you may have a
|
||||
problem with the `bower install` due to a conflict between the versions of angular.js that need to
|
||||
be installed. If you get this then simply delete your `app/bower_components` folder before running
|
||||
`npm install`.
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
If you have bower installed globally then you can run `bower install` but for this project we have
|
||||
**Note:** If you have bower installed globally then you can run `bower install` but for this project we have
|
||||
preconfigured `npm install` to run bower for us.
|
||||
</div>
|
||||
|
||||
@@ -248,7 +255,7 @@ which are described in detail below.
|
||||
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.
|
||||
This time, instead of the `ng-repeat` element, let's add it to the element containing the `ng-view` directive.
|
||||
In order to do this, we'll have to make some small changes to the HTML code so that we can have more control over our
|
||||
animations between view changes.
|
||||
|
||||
@@ -333,13 +340,13 @@ a cross fade animation in between. So as the previous page is just about to be r
|
||||
while the new page fades in right on top of it.
|
||||
|
||||
Once the leave animation is over then element is removed and once the enter animation is complete
|
||||
then the `ng-enter` and `ng-enter-active` CSS classes are removed from the element rendering it to
|
||||
be position itself with its default CSS code (so no more absolute positioning once the animation is
|
||||
then the `ng-enter` and `ng-enter-active` CSS classes are removed from the element, causing it to rerender and
|
||||
reposition itself with its default CSS code (so no more absolute positioning once the animation is
|
||||
over). This works fluidly so that pages flow naturally between route changes without anything
|
||||
jumping around.
|
||||
|
||||
The CSS classes applied (the start and end classes) are much the same as with `ng-repeat`. Each time
|
||||
a new page is loaded the ng-view directive will create a copy of itself, download the template and
|
||||
a new page is loaded the `ng-view` directive will create a copy of itself, download the template and
|
||||
append the contents. This ensures that all views are contained within a single HTML element which
|
||||
allows for easy animation control.
|
||||
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var path = require('canonical-path');
|
||||
var versionInfo = require('../lib/versions/version-info');
|
||||
var basePath = __dirname;
|
||||
|
||||
var basePackage = require('./config');
|
||||
|
||||
module.exports = function(config) {
|
||||
|
||||
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + versionInfo.cdnVersion;
|
||||
|
||||
var getVersion = function(component, sourceFolder, packageFile) {
|
||||
sourceFolder = sourceFolder || './bower_components';
|
||||
packageFile = packageFile || 'bower.json';
|
||||
|
||||
return require(path.resolve(sourceFolder,component,packageFile)).version;
|
||||
};
|
||||
|
||||
|
||||
config = basePackage(config);
|
||||
|
||||
config.set('source.projectPath', path.resolve(basePath, '..'));
|
||||
|
||||
config.set('source.files', [
|
||||
{ pattern: 'src/**/*.js', basePath: path.resolve(basePath,'..') },
|
||||
{ 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');
|
||||
|
||||
config.merge('deployment', {
|
||||
environments: [{
|
||||
name: 'debug',
|
||||
examples: {
|
||||
commonFiles: {
|
||||
scripts: [ '../../../angular.js' ]
|
||||
},
|
||||
dependencyPath: '../../../'
|
||||
},
|
||||
scripts: [
|
||||
'../angular.js',
|
||||
'../angular-resource.js',
|
||||
'../angular-route.js',
|
||||
'../angular-cookies.js',
|
||||
'../angular-sanitize.js',
|
||||
'../angular-touch.js',
|
||||
'../angular-animate.js',
|
||||
'components/marked-' + getVersion('marked', '../node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.js',
|
||||
'js/angular-bootstrap/bootstrap-prettify.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/docs.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/docs.css',
|
||||
'css/animations.css'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'default',
|
||||
examples: {
|
||||
commonFiles: {
|
||||
scripts: [ '../../../angular.min.js' ]
|
||||
},
|
||||
dependencyPath: '../../../'
|
||||
},
|
||||
scripts: [
|
||||
'../angular.min.js',
|
||||
'../angular-resource.min.js',
|
||||
'../angular-route.min.js',
|
||||
'../angular-cookies.min.js',
|
||||
'../angular-sanitize.min.js',
|
||||
'../angular-touch.min.js',
|
||||
'../angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', '../node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.js',
|
||||
'js/angular-bootstrap/bootstrap-prettify.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/docs.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/docs.css',
|
||||
'css/animations.css'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'jquery',
|
||||
examples: {
|
||||
commonFiles: {
|
||||
scripts: [
|
||||
'../../components/jquery-' + getVersion('jquery') + '/jquery.js',
|
||||
'../../../angular.js'
|
||||
]
|
||||
},
|
||||
dependencyPath: '../../../'
|
||||
},
|
||||
scripts: [
|
||||
'components/jquery-' + getVersion('jquery') + '/jquery.js',
|
||||
'../angular.min.js',
|
||||
'../angular-resource.min.js',
|
||||
'../angular-route.min.js',
|
||||
'../angular-cookies.min.js',
|
||||
'../angular-sanitize.min.js',
|
||||
'../angular-touch.min.js',
|
||||
'../angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', '../node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.js',
|
||||
'js/angular-bootstrap/bootstrap-prettify.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/docs.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/docs.css',
|
||||
'css/animations.css'
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'production',
|
||||
examples: {
|
||||
commonFiles: {
|
||||
scripts: [ cdnUrl + '/angular.min.js' ]
|
||||
},
|
||||
dependencyPath: cdnUrl + '/'
|
||||
},
|
||||
scripts: [
|
||||
cdnUrl + '/angular.min.js',
|
||||
cdnUrl + '/angular-resource.min.js',
|
||||
cdnUrl + '/angular-route.min.js',
|
||||
cdnUrl + '/angular-cookies.min.js',
|
||||
cdnUrl + '/angular-sanitize.min.js',
|
||||
cdnUrl + '/angular-touch.min.js',
|
||||
cdnUrl + '/angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', '../node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.js',
|
||||
'js/angular-bootstrap/bootstrap-prettify.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/docs.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
'components/open-sans-fontface-' + getVersion('open-sans-fontface') + '/open-sans.css',
|
||||
'css/prettify-theme.css',
|
||||
'css/docs.css',
|
||||
'css/animations.css'
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
return config;
|
||||
};
|
||||
+42
-11
@@ -5,10 +5,13 @@ var log = require('gulp-util').log;
|
||||
var concat = require('gulp-concat');
|
||||
var jshint = require('gulp-jshint');
|
||||
var bower = require('bower');
|
||||
var dgeni = require('dgeni');
|
||||
var Dgeni = require('dgeni');
|
||||
var merge = require('event-stream').merge;
|
||||
var path = require('canonical-path');
|
||||
|
||||
var foreach = require('gulp-foreach');
|
||||
var uglify = require('gulp-uglify');
|
||||
var sourcemaps = require('gulp-sourcemaps');
|
||||
var rename = require('gulp-rename');
|
||||
|
||||
// We indicate to gulp that tasks are async by returning the stream.
|
||||
// Gulp can then wait for the stream to close before starting dependent tasks.
|
||||
@@ -17,6 +20,9 @@ var path = require('canonical-path');
|
||||
var outputFolder = '../build/docs';
|
||||
var bowerFolder = 'bower_components';
|
||||
|
||||
var src = 'app/src/**/*.js';
|
||||
var assets = 'app/assets/**/*';
|
||||
|
||||
|
||||
var copyComponent = function(component, pattern, sourceFolder, packageFile) {
|
||||
pattern = pattern || '/**/*';
|
||||
@@ -40,14 +46,37 @@ gulp.task('bower', function() {
|
||||
});
|
||||
|
||||
gulp.task('build-app', function() {
|
||||
gulp.src('app/src/**/*.js')
|
||||
.pipe(concat('docs.js'))
|
||||
.pipe(gulp.dest(outputFolder + '/js/'));
|
||||
var file = 'docs.js';
|
||||
var minFile = 'docs.min.js';
|
||||
var folder = outputFolder + '/js/';
|
||||
|
||||
return gulp.src(src)
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(concat(file))
|
||||
.pipe(gulp.dest(folder))
|
||||
.pipe(rename(minFile))
|
||||
.pipe(uglify())
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest(folder));
|
||||
});
|
||||
|
||||
gulp.task('assets', ['bower'], function() {
|
||||
var JS_EXT = /\.js$/;
|
||||
return merge(
|
||||
gulp.src(['app/assets/**/*']).pipe(gulp.dest(outputFolder)),
|
||||
gulp.src([assets])
|
||||
.pipe(gulp.dest(outputFolder)),
|
||||
gulp.src([assets])
|
||||
.pipe(foreach(function(stream, file) {
|
||||
if (JS_EXT.test(file.relative)) {
|
||||
var minFile = file.relative.replace(JS_EXT, '.min.js');
|
||||
return stream
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(concat(minFile))
|
||||
.pipe(uglify())
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest(outputFolder));
|
||||
}
|
||||
})),
|
||||
copyComponent('bootstrap', '/dist/**/*'),
|
||||
copyComponent('open-sans-fontface'),
|
||||
copyComponent('lunr.js','/*.js'),
|
||||
@@ -59,11 +88,10 @@ gulp.task('assets', ['bower'], function() {
|
||||
|
||||
|
||||
gulp.task('doc-gen', ['bower'], function() {
|
||||
var generateDocs = dgeni.generator('docs.config.js');
|
||||
return generateDocs()
|
||||
.catch(function(error) {
|
||||
process.exit(1);
|
||||
});
|
||||
var dgeni = new Dgeni([require('./config')]);
|
||||
return dgeni.generate().catch(function(error) {
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
// JSHint the example and protractor test files
|
||||
@@ -78,3 +106,6 @@ gulp.task('jshint', ['doc-gen'], function() {
|
||||
// The default task that will be run if no task is supplied
|
||||
gulp.task('default', ['assets', 'doc-gen', 'build-app', 'jshint']);
|
||||
|
||||
gulp.task('watch', function() {
|
||||
gulp.watch([src, assets], ['assets', 'build-app']);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
var config = require('../protractor-shared-conf').config;
|
||||
|
||||
config.specs = [
|
||||
'app/e2e/**/*.scenario.js'
|
||||
];
|
||||
|
||||
config.capabilities = {
|
||||
browserName: 'chrome',
|
||||
};
|
||||
|
||||
exports.config = config;
|
||||
@@ -319,7 +319,6 @@ goog.provide('goog.i18n.DateTimeSymbols_fur');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_fur_IT');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_fy');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_fy_NL');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_ga');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_ga_IE');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_gd');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_gd_GB');
|
||||
@@ -10770,9 +10769,9 @@ goog.i18n.DateTimeSymbols_fy_NL = goog.i18n.DateTimeSymbols_fy;
|
||||
|
||||
|
||||
/**
|
||||
* Date/time formatting symbols for locale ga.
|
||||
* Date/time formatting symbols for locale ga_IE.
|
||||
*/
|
||||
goog.i18n.DateTimeSymbols_ga = {
|
||||
goog.i18n.DateTimeSymbols_ga_IE = {
|
||||
ERAS: ['RC', 'AD'],
|
||||
ERANAMES: ['Roimh Chríost', 'Anno Domini'],
|
||||
NARROWMONTHS: ['E', 'F', 'M', 'A', 'B', 'M', 'I', 'L', 'M', 'D', 'S', 'N'],
|
||||
@@ -10809,12 +10808,6 @@ goog.i18n.DateTimeSymbols_ga = {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Date/time formatting symbols for locale ga_IE.
|
||||
*/
|
||||
goog.i18n.DateTimeSymbols_ga_IE = goog.i18n.DateTimeSymbols_ga;
|
||||
|
||||
|
||||
/**
|
||||
* Date/time formatting symbols for locale gd.
|
||||
*/
|
||||
@@ -21329,12 +21322,8 @@ if (goog.LOCALE == 'fy_NL' || goog.LOCALE == 'fy-NL') {
|
||||
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fy;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'ga') {
|
||||
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ga;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'ga_IE' || goog.LOCALE == 'ga-IE') {
|
||||
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ga;
|
||||
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ga_IE;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'gd') {
|
||||
|
||||
@@ -67,6 +67,7 @@ goog.provide('goog.i18n.DateTimeSymbols_fi');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_fil');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_fr');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_fr_CA');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_ga');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_gl');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_gsw');
|
||||
goog.provide('goog.i18n.DateTimeSymbols_gu');
|
||||
@@ -1409,6 +1410,46 @@ goog.i18n.DateTimeSymbols_fr_CA = {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Date/time formatting symbols for locale ga.
|
||||
*/
|
||||
goog.i18n.DateTimeSymbols_ga = {
|
||||
ERAS: ['RC', 'AD'],
|
||||
ERANAMES: ['Roimh Chríost', 'Anno Domini'],
|
||||
NARROWMONTHS: ['E', 'F', 'M', 'A', 'B', 'M', 'I', 'L', 'M', 'D', 'S', 'N'],
|
||||
STANDALONENARROWMONTHS: ['E', 'F', 'M', 'A', 'B', 'M', 'I', 'L', 'M', 'D',
|
||||
'S', 'N'],
|
||||
MONTHS: ['Eanáir', 'Feabhra', 'Márta', 'Aibreán', 'Bealtaine', 'Meitheamh',
|
||||
'Iúil', 'Lúnasa', 'Meán Fómhair', 'Deireadh Fómhair', 'Samhain',
|
||||
'Nollaig'],
|
||||
STANDALONEMONTHS: ['Eanáir', 'Feabhra', 'Márta', 'Aibreán', 'Bealtaine',
|
||||
'Meitheamh', 'Iúil', 'Lúnasa', 'Meán Fómhair', 'Deireadh Fómhair',
|
||||
'Samhain', 'Nollaig'],
|
||||
SHORTMONTHS: ['Ean', 'Feabh', 'Márta', 'Aib', 'Beal', 'Meith', 'Iúil',
|
||||
'Lún', 'MFómh', 'DFómh', 'Samh', 'Noll'],
|
||||
STANDALONESHORTMONTHS: ['Ean', 'Feabh', 'Márta', 'Aib', 'Beal', 'Meith',
|
||||
'Iúil', 'Lún', 'MFómh', 'DFómh', 'Samh', 'Noll'],
|
||||
WEEKDAYS: ['Dé Domhnaigh', 'Dé Luain', 'Dé Máirt', 'Dé Céadaoin',
|
||||
'Déardaoin', 'Dé hAoine', 'Dé Sathairn'],
|
||||
STANDALONEWEEKDAYS: ['Dé Domhnaigh', 'Dé Luain', 'Dé Máirt',
|
||||
'Dé Céadaoin', 'Déardaoin', 'Dé hAoine', 'Dé Sathairn'],
|
||||
SHORTWEEKDAYS: ['Domh', 'Luan', 'Máirt', 'Céad', 'Déar', 'Aoine', 'Sath'],
|
||||
STANDALONESHORTWEEKDAYS: ['Domh', 'Luan', 'Máirt', 'Céad', 'Déar', 'Aoine',
|
||||
'Sath'],
|
||||
NARROWWEEKDAYS: ['D', 'L', 'M', 'C', 'D', 'A', 'S'],
|
||||
STANDALONENARROWWEEKDAYS: ['D', 'L', 'M', 'C', 'D', 'A', 'S'],
|
||||
SHORTQUARTERS: ['R1', 'R2', 'R3', 'R4'],
|
||||
QUARTERS: ['1ú ráithe', '2ú ráithe', '3ú ráithe', '4ú ráithe'],
|
||||
AMPMS: ['a.m.', 'p.m.'],
|
||||
DATEFORMATS: ['EEEE d MMMM y', 'd MMMM y', 'd MMM y', 'dd/MM/y'],
|
||||
TIMEFORMATS: ['HH:mm:ss zzzz', 'HH:mm:ss z', 'HH:mm:ss', 'HH:mm'],
|
||||
DATETIMEFORMATS: ['{1} {0}', '{1} {0}', '{1} {0}', '{1} {0}'],
|
||||
FIRSTDAYOFWEEK: 6,
|
||||
WEEKENDRANGE: [5, 6],
|
||||
FIRSTWEEKCUTOFFDAY: 2
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Date/time formatting symbols for locale gl.
|
||||
*/
|
||||
@@ -4307,6 +4348,8 @@ if (goog.LOCALE == 'af') {
|
||||
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr;
|
||||
} else if (goog.LOCALE == 'fr_CA' || goog.LOCALE == 'fr-CA') {
|
||||
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_fr_CA;
|
||||
} else if (goog.LOCALE == 'ga') {
|
||||
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_ga;
|
||||
} else if (goog.LOCALE == 'gl') {
|
||||
goog.i18n.DateTimeSymbols = goog.i18n.DateTimeSymbols_gl;
|
||||
} else if (goog.LOCALE == 'gsw') {
|
||||
|
||||
@@ -123,6 +123,8 @@ goog.provide('goog.i18n.NumberFormatSymbols_fr_MQ');
|
||||
goog.provide('goog.i18n.NumberFormatSymbols_fr_PM');
|
||||
goog.provide('goog.i18n.NumberFormatSymbols_fr_RE');
|
||||
goog.provide('goog.i18n.NumberFormatSymbols_fr_YT');
|
||||
goog.provide('goog.i18n.NumberFormatSymbols_ga');
|
||||
goog.provide('goog.i18n.NumberFormatSymbols_ga_IE');
|
||||
goog.provide('goog.i18n.NumberFormatSymbols_gl');
|
||||
goog.provide('goog.i18n.NumberFormatSymbols_gl_ES');
|
||||
goog.provide('goog.i18n.NumberFormatSymbols_gsw');
|
||||
@@ -1377,6 +1379,36 @@ goog.i18n.NumberFormatSymbols_fr_RE = goog.i18n.NumberFormatSymbols_fr;
|
||||
goog.i18n.NumberFormatSymbols_fr_YT = goog.i18n.NumberFormatSymbols_fr;
|
||||
|
||||
|
||||
/**
|
||||
* Number formatting symbols for locale ga.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.i18n.NumberFormatSymbols_ga = {
|
||||
DECIMAL_SEP: '.',
|
||||
GROUP_SEP: ',',
|
||||
PERCENT: '%',
|
||||
ZERO_DIGIT: '0',
|
||||
PLUS_SIGN: '+',
|
||||
MINUS_SIGN: '-',
|
||||
EXP_SYMBOL: 'E',
|
||||
PERMILL: '\u2030',
|
||||
INFINITY: '\u221E',
|
||||
NAN: 'NaN',
|
||||
DECIMAL_PATTERN: '#,##0.###',
|
||||
SCIENTIFIC_PATTERN: '#E0',
|
||||
PERCENT_PATTERN: '#,##0%',
|
||||
CURRENCY_PATTERN: '\u00A4#,##0.00',
|
||||
DEF_CURRENCY_CODE: 'EUR'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Number formatting symbols for locale ga_IE.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.i18n.NumberFormatSymbols_ga_IE = goog.i18n.NumberFormatSymbols_ga;
|
||||
|
||||
|
||||
/**
|
||||
* Number formatting symbols for locale gl.
|
||||
* @enum {string}
|
||||
@@ -3613,6 +3645,14 @@ if (goog.LOCALE == 'fr_YT' || goog.LOCALE == 'fr-YT') {
|
||||
goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_fr;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'ga') {
|
||||
goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ga;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'ga_IE' || goog.LOCALE == 'ga-IE') {
|
||||
goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_ga;
|
||||
}
|
||||
|
||||
if (goog.LOCALE == 'gl') {
|
||||
goog.i18n.NumberFormatSymbols = goog.i18n.NumberFormatSymbols_gl;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -894,6 +894,9 @@ if (goog.LOCALE == 'fr') {
|
||||
if (goog.LOCALE == 'fr_CA' || goog.LOCALE == 'fr-CA') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.frSelect_;
|
||||
}
|
||||
if (goog.LOCALE == 'ga') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.gaSelect_;
|
||||
}
|
||||
if (goog.LOCALE == 'gl') {
|
||||
goog.i18n.pluralRules.select = goog.i18n.pluralRules.enSelect_;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,12 @@ function readSymbols() {
|
||||
.then(function(content) {
|
||||
var currencySymbols = closureI18nExtractor.extractCurrencySymbols(content);
|
||||
return qfs.read(__dirname + '/../closure/numberSymbols.js', 'b').then(function(content) {
|
||||
closureI18nExtractor.extractNumberSymbols(content, localeInfo, currencySymbols);
|
||||
var numberSymbols = content;
|
||||
return qfs.read(__dirname + '/../closure/numberSymbolsExt.js', 'b')
|
||||
.then(function(content) {
|
||||
numberSymbols += content;
|
||||
return closureI18nExtractor.extractNumberSymbols(numberSymbols, localeInfo, currencySymbols);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ function parsePattern(pattern) {
|
||||
var p = {
|
||||
minInt: 1,
|
||||
minFrac: 0,
|
||||
macFrac: 0,
|
||||
maxFrac: 0,
|
||||
posPre: '',
|
||||
posSuf: '',
|
||||
negPre: '',
|
||||
|
||||
@@ -14,4 +14,5 @@ curl "$I18N_BASE/currency.js" > closure/currencySymbols.js
|
||||
curl "$I18N_BASE/datetimesymbols.js" > closure/datetimeSymbols.js
|
||||
curl "$I18N_BASE/datetimesymbolsext.js" > closure/datetimeSymbolsExt.js
|
||||
curl "$I18N_BASE/numberformatsymbols.js" > closure/numberSymbols.js
|
||||
curl "$I18N_BASE/numberformatsymbolsext.js" > closure/numberSymbolsExt.js
|
||||
curl "$I18N_BASE/pluralrules.js" > closure/pluralRules.js
|
||||
|
||||
+8
-4
@@ -285,11 +285,15 @@ module.exports = {
|
||||
|
||||
|
||||
//csp connect middleware
|
||||
csp: function(){
|
||||
conditionalCsp: function(){
|
||||
return function(req, res, next){
|
||||
res.setHeader("X-WebKit-CSP", "default-src 'self';");
|
||||
res.setHeader("X-Content-Security-Policy", "default-src 'self'");
|
||||
res.setHeader("Content-Security-Policy", "default-src 'self'");
|
||||
var CSP = /\.csp\W/;
|
||||
|
||||
if (CSP.test(req.url)) {
|
||||
res.setHeader("X-WebKit-CSP", "default-src 'self';");
|
||||
res.setHeader("X-Content-Security-Policy", "default-src 'self'");
|
||||
res.setHeader("Content-Security-Policy", "default-src 'self'");
|
||||
}
|
||||
next();
|
||||
};
|
||||
},
|
||||
|
||||
@@ -83,6 +83,7 @@ var getTaggedVersion = function() {
|
||||
if ( version && semver.satisfies(version, currentPackage.branchVersion)) {
|
||||
version.codeName = getCodeName(tag);
|
||||
version.full = version.version;
|
||||
version.branch = 'v' + currentPackage.branchVersion.replace('*', 'x');
|
||||
return version;
|
||||
}
|
||||
}
|
||||
@@ -197,6 +198,7 @@ var getSnapshotVersion = function() {
|
||||
version.isSnapshot = true;
|
||||
version.format();
|
||||
version.full = version.version + '+' + version.build;
|
||||
version.branch = 'master';
|
||||
|
||||
return version;
|
||||
};
|
||||
|
||||
Generated
+890
-580
File diff suppressed because it is too large
Load Diff
+9
-8
@@ -11,9 +11,8 @@
|
||||
"bower": "~1.3.9",
|
||||
"browserstacktunnel-wrapper": "~1.1.1",
|
||||
"canonical-path": "0.0.2",
|
||||
"dgeni": "^0.3.0",
|
||||
"dgeni-packages": "^0.9.8",
|
||||
"es6-shim": "^0.14.0",
|
||||
"dgeni": "^0.4.0",
|
||||
"dgeni-packages": "^0.10.0",
|
||||
"event-stream": "~3.1.0",
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-bump": "~0.0.13",
|
||||
@@ -29,8 +28,12 @@
|
||||
"grunt-parallel": "~0.3.1",
|
||||
"grunt-shell": "~0.4.0",
|
||||
"gulp": "~3.8.0",
|
||||
"gulp-concat": "~2.1.7",
|
||||
"gulp-concat": "^2.4.1",
|
||||
"gulp-foreach": "0.0.1",
|
||||
"gulp-jshint": "~1.4.2",
|
||||
"gulp-rename": "^1.2.0",
|
||||
"gulp-sourcemaps": "^1.2.2",
|
||||
"gulp-uglify": "^1.0.1",
|
||||
"gulp-util": "^3.0.1",
|
||||
"jasmine-node": "~1.11.0",
|
||||
"jasmine-reporters": "~0.2.1",
|
||||
@@ -49,7 +52,7 @@
|
||||
"marked": "~0.3.0",
|
||||
"node-html-encoder": "0.0.2",
|
||||
"promises-aplus-tests": "~2.0.4",
|
||||
"protractor": "1.2.0",
|
||||
"protractor": "1.3.1",
|
||||
"q": "~1.0.0",
|
||||
"q-io": "^1.10.9",
|
||||
"qq": "^0.3.5",
|
||||
@@ -57,9 +60,7 @@
|
||||
"semver": "~2.1.0",
|
||||
"shelljs": "~0.2.6",
|
||||
"sorted-object": "^1.0.0",
|
||||
"stringmap": "^0.2.2",
|
||||
"winston": "~0.7.2",
|
||||
"yaml-js": "~0.0.8"
|
||||
"stringmap": "^0.2.2"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ var config = require('./protractor-shared-conf').config;
|
||||
|
||||
config.specs = [
|
||||
'build/docs/ptore2e/**/*.js',
|
||||
'docs/app/e2e/docsAppE2E.js'
|
||||
'docs/app/e2e/**/*.scenario.js'
|
||||
];
|
||||
|
||||
config.capabilities = {
|
||||
|
||||
@@ -5,7 +5,7 @@ exports.config = {
|
||||
|
||||
specs: [
|
||||
'build/docs/ptore2e/**/*.js',
|
||||
'docs/app/e2e/docsAppE2E.js'
|
||||
'docs/app/e2e/*.scenario.js'
|
||||
],
|
||||
|
||||
capabilities: {
|
||||
@@ -21,9 +21,9 @@ exports.config = {
|
||||
|
||||
// Disable animations so e2e tests run more quickly
|
||||
var disableNgAnimate = function() {
|
||||
angular.module('disableNgAnimate', []).run(function($animate) {
|
||||
angular.module('disableNgAnimate', []).run(['$animate', function($animate) {
|
||||
$animate.enabled(false);
|
||||
});
|
||||
}]);
|
||||
};
|
||||
|
||||
browser.addMockModule('disableNgAnimate', disableNgAnimate);
|
||||
|
||||
@@ -12,9 +12,9 @@ exports.config = {
|
||||
|
||||
// Disable animations so e2e tests run more quickly
|
||||
var disableNgAnimate = function() {
|
||||
angular.module('disableNgAnimate', []).run(function($animate) {
|
||||
angular.module('disableNgAnimate', []).run(['$animate', function($animate) {
|
||||
$animate.enabled(false);
|
||||
});
|
||||
}]);
|
||||
};
|
||||
|
||||
browser.addMockModule('disableNgAnimate', disableNgAnimate);
|
||||
|
||||
@@ -17,6 +17,7 @@ function init {
|
||||
REPOS=(
|
||||
angular
|
||||
angular-animate
|
||||
angular-aria
|
||||
angular-cookies
|
||||
angular-i18n
|
||||
angular-loader
|
||||
|
||||
@@ -9,11 +9,11 @@ if [ $JOB = "unit" ]; then
|
||||
grunt test:promises-aplus
|
||||
grunt test:unit --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
|
||||
grunt tests:docs --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
|
||||
grunt test:travis-protractor --specs "docs/app/e2e/docsAppE2E.js"
|
||||
grunt test:travis-protractor --specs "docs/app/e2e/**/*.scenario.js"
|
||||
elif [ $JOB = "e2e" ]; then
|
||||
export TARGET_SPECS="build/docs/ptore2e/**/*jqlite_test.js"
|
||||
export TARGET_SPECS="build/docs/ptore2e/**/default_test.js"
|
||||
if [ $TEST_TARGET = "jquery" ]; then
|
||||
TARGET_SPECS="build/docs/ptore2e/**/*jquery_test.js"
|
||||
TARGET_SPECS="build/docs/ptore2e/**/jquery_test.js"
|
||||
fi
|
||||
grunt test:travis-protractor --specs "$TARGET_SPECS"
|
||||
else
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
"trim": false,
|
||||
"isElement": false,
|
||||
"makeMap": false,
|
||||
"map": false,
|
||||
"size": false,
|
||||
"includes": false,
|
||||
"arrayRemove": false,
|
||||
|
||||
+2
-12
@@ -50,7 +50,6 @@
|
||||
trim: true,
|
||||
isElement: true,
|
||||
makeMap: true,
|
||||
map: true,
|
||||
size: true,
|
||||
includes: true,
|
||||
arrayRemove: true,
|
||||
@@ -327,7 +326,7 @@ function setHashKey(obj, h) {
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Extends the destination object `dst` by copying all of the properties from the `src` object(s)
|
||||
* Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
|
||||
* to `dst`. You can specify multiple `src` objects.
|
||||
*
|
||||
* @param {Object} dst Destination object.
|
||||
@@ -617,15 +616,6 @@ function nodeName_(element) {
|
||||
}
|
||||
|
||||
|
||||
function map(obj, iterator, context) {
|
||||
var results = [];
|
||||
forEach(obj, function(value, index, list) {
|
||||
results.push(iterator.call(context, value, index, list));
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @description
|
||||
* Determines the number of elements in an array, the number of properties an object has, or
|
||||
@@ -1454,7 +1444,7 @@ function reloadWithDebugInfo() {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* @name angular.getTestability
|
||||
* @module ng
|
||||
* @description
|
||||
|
||||
+27
-5
@@ -694,7 +694,6 @@ forEach({
|
||||
|
||||
function createEventHandler(element, events) {
|
||||
var eventHandler = function (event, type) {
|
||||
|
||||
// jQuery specific api
|
||||
event.isDefaultPrevented = function() {
|
||||
return event.defaultPrevented;
|
||||
@@ -705,13 +704,34 @@ function createEventHandler(element, events) {
|
||||
|
||||
if (!eventFnsLength) return;
|
||||
|
||||
if (isUndefined(event.immediatePropagationStopped)) {
|
||||
var originalStopImmediatePropagation = event.stopImmediatePropagation;
|
||||
event.stopImmediatePropagation = function() {
|
||||
event.immediatePropagationStopped = true;
|
||||
|
||||
if (event.stopPropagation) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (originalStopImmediatePropagation) {
|
||||
originalStopImmediatePropagation.call(event);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
event.isImmediatePropagationStopped = function() {
|
||||
return event.immediatePropagationStopped === true;
|
||||
};
|
||||
|
||||
// Copy event handlers in case event handlers array is modified during execution.
|
||||
if ((eventFnsLength > 1)) {
|
||||
eventFns = shallowCopy(eventFns);
|
||||
}
|
||||
|
||||
for (var i = 0; i < eventFnsLength; i++) {
|
||||
eventFns[i].call(element, event);
|
||||
if (!event.isImmediatePropagationStopped()) {
|
||||
eventFns[i].call(element, event);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -912,11 +932,12 @@ forEach({
|
||||
var eventFns = events && events[eventName];
|
||||
|
||||
if (eventFns) {
|
||||
|
||||
// Create a dummy event to pass to the handlers
|
||||
dummyEvent = {
|
||||
preventDefault: function() { this.defaultPrevented = true; },
|
||||
isDefaultPrevented: function() { return this.defaultPrevented === true; },
|
||||
stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
|
||||
isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
|
||||
stopPropagation: noop,
|
||||
type: eventName,
|
||||
target: element
|
||||
@@ -932,9 +953,10 @@ forEach({
|
||||
handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
|
||||
|
||||
forEach(eventFnsCopy, function(fn) {
|
||||
fn.apply(element, handlerArgs);
|
||||
if (!dummyEvent.isImmediatePropagationStopped()) {
|
||||
fn.apply(element, handlerArgs);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}, function(fn, name){
|
||||
|
||||
@@ -158,8 +158,6 @@ function Browser(window, document, $log, $sniffer) {
|
||||
if (replace) history.replaceState(null, '', url);
|
||||
else {
|
||||
history.pushState(null, '', url);
|
||||
// Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462
|
||||
baseElement.attr('href', baseElement.attr('href'));
|
||||
}
|
||||
} else {
|
||||
newLocation = url;
|
||||
|
||||
+120
-45
@@ -212,8 +212,11 @@
|
||||
* * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
|
||||
* * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
|
||||
* * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
|
||||
* * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
|
||||
* * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
|
||||
* `null` to the `link` fn if not found.
|
||||
* * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
|
||||
* `null` to the `link` fn if not found.
|
||||
*
|
||||
*
|
||||
* #### `controllerAs`
|
||||
@@ -294,13 +297,20 @@
|
||||
* compile the content of the element and make it available to the directive.
|
||||
* Typically used with {@link ng.directive:ngTransclude
|
||||
* ngTransclude}. The advantage of transclusion is that the linking function receives a
|
||||
* transclusion function which is pre-bound to the correct scope. In a typical setup the widget
|
||||
* creates an `isolate` scope, but the transclusion is not a child, but a sibling of the `isolate`
|
||||
* scope. This makes it possible for the widget to have private state, and the transclusion to
|
||||
* be bound to the parent (pre-`isolate`) scope.
|
||||
* transclusion function which is pre-bound to the scope of the position in the DOM from where
|
||||
* it was taken.
|
||||
*
|
||||
* * `true` - transclude the content of the directive.
|
||||
* * `'element'` - transclude the whole element including any directives defined at lower priority.
|
||||
* In a typical setup the widget creates an `isolate` scope, but the transcluded
|
||||
* content has its own **transclusion scope**. While the **transclusion scope** is owned as a child,
|
||||
* by the **isolate scope**, it prototypically inherits from the original scope from where the
|
||||
* transcluded content was taken.
|
||||
*
|
||||
* This makes it possible for the widget to have private state, and the transclusion to
|
||||
* be bound to the original (pre-`isolate`) scope.
|
||||
*
|
||||
* * `true` - transclude the content (i.e. the child nodes) of the directive's element.
|
||||
* * `'element'` - transclude the whole of the directive's element including any directives on this
|
||||
* element that defined at a lower priority than this directive.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note:** When testing an element transclude directive you must not place the directive at the root of the
|
||||
@@ -404,7 +414,6 @@
|
||||
* It is safe to do DOM transformation in the post-linking function on elements that are not waiting
|
||||
* for their async templates to be resolved.
|
||||
*
|
||||
* <a name="Attributes"></a>
|
||||
* ### Attributes
|
||||
*
|
||||
* The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
|
||||
@@ -442,7 +451,7 @@
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Below is an example using `$compileProvider`.
|
||||
* ## Example
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note**: Typically directives are registered with `module.directive`. The example below is
|
||||
@@ -567,13 +576,39 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
Suffix = 'Directive',
|
||||
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w_\-]+)\s+(.*)$/,
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w_\-]+)(?:\:([^;]+))?;?)/,
|
||||
ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset');
|
||||
ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
|
||||
REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
|
||||
|
||||
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
|
||||
// The assumption is that future DOM event attribute names will begin with
|
||||
// 'on' and be composed of only English letters.
|
||||
var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
|
||||
|
||||
function parseIsolateBindings(scope, directiveName) {
|
||||
var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/;
|
||||
|
||||
var bindings = {};
|
||||
|
||||
forEach(scope, function(definition, scopeName) {
|
||||
var match = definition.match(LOCAL_REGEXP);
|
||||
|
||||
if (!match) {
|
||||
throw $compileMinErr('iscp',
|
||||
"Invalid isolate scope definition for directive '{0}'." +
|
||||
" Definition: {... {1}: '{2}' ...}",
|
||||
directiveName, scopeName, definition);
|
||||
}
|
||||
|
||||
bindings[scopeName] = {
|
||||
attrName: match[3] || scopeName,
|
||||
mode: match[1],
|
||||
optional: match[2] === '?'
|
||||
};
|
||||
});
|
||||
|
||||
return bindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $compileProvider#directive
|
||||
@@ -611,6 +646,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
directive.name = directive.name || name;
|
||||
directive.require = directive.require || (directive.controller && directive.name);
|
||||
directive.restrict = directive.restrict || 'EA';
|
||||
if (isObject(directive.scope)) {
|
||||
directive.$$isolateBindings = parseIsolateBindings(directive.scope, directive.name);
|
||||
}
|
||||
directives.push(directive);
|
||||
} catch (e) {
|
||||
$exceptionHandler(e);
|
||||
@@ -844,10 +882,44 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
nodeName = nodeName_(this.$$element);
|
||||
|
||||
// sanitize a[href] and img[src] values
|
||||
if ((nodeName === 'a' && key === 'href') ||
|
||||
(nodeName === 'img' && key === 'src')) {
|
||||
// sanitize a[href] and img[src] values
|
||||
this[key] = value = $$sanitizeUri(value, key === 'src');
|
||||
} else if (nodeName === 'img' && key === 'srcset') {
|
||||
// sanitize img[srcset] values
|
||||
var result = "";
|
||||
|
||||
// first check if there are spaces because it's not the same pattern
|
||||
var trimmedSrcset = trim(value);
|
||||
// ( 999x ,| 999w ,| ,|, )
|
||||
var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
|
||||
var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
|
||||
|
||||
// split srcset into tuple of uri and descriptor except for the last item
|
||||
var rawUris = trimmedSrcset.split(pattern);
|
||||
|
||||
// for each tuples
|
||||
var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
|
||||
for (var i=0; i<nbrUrisWith2parts; i++) {
|
||||
var innerIdx = i*2;
|
||||
// sanitize the uri
|
||||
result += $$sanitizeUri(trim( rawUris[innerIdx]), true);
|
||||
// add the descriptor
|
||||
result += ( " " + trim(rawUris[innerIdx+1]));
|
||||
}
|
||||
|
||||
// split the last item into uri and descriptor
|
||||
var lastTuple = trim(rawUris[i*2]).split(/\s/);
|
||||
|
||||
// sanitize the last uri
|
||||
result += $$sanitizeUri(trim(lastTuple[0]), true);
|
||||
|
||||
// and add the last descriptor if any
|
||||
if( lastTuple.length === 2) {
|
||||
result += (" " + trim(lastTuple[1]));
|
||||
}
|
||||
this[key] = value = result;
|
||||
}
|
||||
|
||||
if (writeAttr !== false) {
|
||||
@@ -885,12 +957,12 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
* @param {string} key Normalized key. (ie ngAttribute) .
|
||||
* @param {function(interpolatedValue)} fn Function that will be called whenever
|
||||
the interpolated value of the attribute changes.
|
||||
* See the {@link guide/directive#Attributes Directives} guide for more info.
|
||||
* See {@link ng.$compile#attributes $compile} for more info.
|
||||
* @returns {function()} Returns a deregistration function for this observer.
|
||||
*/
|
||||
$observe: function(key, fn) {
|
||||
var attrs = this,
|
||||
$$observers = (attrs.$$observers || (attrs.$$observers = {})),
|
||||
$$observers = (attrs.$$observers || (attrs.$$observers = Object.create(null))),
|
||||
listeners = ($$observers[key] || ($$observers[key] = []));
|
||||
|
||||
listeners.push(fn);
|
||||
@@ -1138,20 +1210,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) {
|
||||
|
||||
var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement) {
|
||||
var scopeCreated = false;
|
||||
var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
|
||||
|
||||
if (!transcludedScope) {
|
||||
transcludedScope = scope.$new();
|
||||
transcludedScope = scope.$new(false, containingScope);
|
||||
transcludedScope.$$transcluded = true;
|
||||
scopeCreated = true;
|
||||
}
|
||||
|
||||
var clone = transcludeFn(transcludedScope, cloneFn, controllers, previousBoundTranscludeFn, futureParentElement);
|
||||
if (scopeCreated && !elementTransclusion) {
|
||||
clone.on('$destroy', function() { transcludedScope.$destroy(); });
|
||||
}
|
||||
return clone;
|
||||
return transcludeFn(transcludedScope, cloneFn, controllers, previousBoundTranscludeFn, futureParentElement);
|
||||
};
|
||||
|
||||
return boundTranscludeFn;
|
||||
@@ -1561,14 +1627,26 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
function getControllers(directiveName, require, $element, elementControllers) {
|
||||
var value, retrievalMethod = 'data', optional = false;
|
||||
var $searchElement = $element;
|
||||
var match;
|
||||
if (isString(require)) {
|
||||
while((value = require.charAt(0)) == '^' || value == '?') {
|
||||
require = require.substr(1);
|
||||
if (value == '^') {
|
||||
retrievalMethod = 'inheritedData';
|
||||
}
|
||||
optional = optional || value == '?';
|
||||
match = require.match(REQUIRE_PREFIX_REGEXP);
|
||||
require = require.substring(match[0].length);
|
||||
|
||||
if (match[3]) {
|
||||
if (match[1]) match[3] = null;
|
||||
else match[1] = match[3];
|
||||
}
|
||||
if (match[1] === '^') {
|
||||
retrievalMethod = 'inheritedData';
|
||||
} else if (match[1] === '^^') {
|
||||
retrievalMethod = 'inheritedData';
|
||||
$searchElement = $element.parent();
|
||||
}
|
||||
if (match[2] === '?') {
|
||||
optional = true;
|
||||
}
|
||||
|
||||
value = null;
|
||||
|
||||
if (elementControllers && retrievalMethod === 'data') {
|
||||
@@ -1576,7 +1654,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
value = value.instance;
|
||||
}
|
||||
}
|
||||
value = value || $element[retrievalMethod]('$' + require + 'Controller');
|
||||
value = value || $searchElement[retrievalMethod]('$' + require + 'Controller');
|
||||
|
||||
if (!value && !optional) {
|
||||
throw $compileMinErr('ctreq',
|
||||
@@ -1657,21 +1735,19 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
newIsolateScopeDirective.bindToController === true) {
|
||||
isolateBindingContext = isolateScopeController.instance;
|
||||
}
|
||||
forEach(newIsolateScopeDirective.scope, function(definition, scopeName) {
|
||||
var match = definition.match(LOCAL_REGEXP) || [],
|
||||
attrName = match[3] || scopeName,
|
||||
optional = (match[2] == '?'),
|
||||
mode = match[1], // @, =, or &
|
||||
|
||||
forEach(isolateScope.$$isolateBindings = newIsolateScopeDirective.$$isolateBindings, function(definition, scopeName) {
|
||||
var attrName = definition.attrName,
|
||||
optional = definition.optional,
|
||||
mode = definition.mode, // @, =, or &
|
||||
lastValue,
|
||||
parentGet, parentSet, compare;
|
||||
|
||||
isolateScope.$$isolateBindings[scopeName] = mode + attrName;
|
||||
|
||||
switch (mode) {
|
||||
|
||||
case '@':
|
||||
attrs.$observe(attrName, function(value) {
|
||||
isolateScope[scopeName] = value;
|
||||
isolateBindingContext[scopeName] = value;
|
||||
});
|
||||
attrs.$$observers[attrName].$$scope = scope;
|
||||
if( attrs[attrName] ) {
|
||||
@@ -1699,7 +1775,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
attrs[attrName], newIsolateScopeDirective.name);
|
||||
};
|
||||
lastValue = isolateBindingContext[scopeName] = parentGet(scope);
|
||||
var unwatch = scope.$watch($parse(attrs[attrName], function parentValueWatch(parentValue) {
|
||||
var parentValueWatch = function parentValueWatch(parentValue) {
|
||||
if (!compare(parentValue, isolateBindingContext[scopeName])) {
|
||||
// we are out of sync and need to copy
|
||||
if (!compare(parentValue, lastValue)) {
|
||||
@@ -1711,7 +1787,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
}
|
||||
}
|
||||
return lastValue = parentValue;
|
||||
}), null, parentGet.literal);
|
||||
};
|
||||
parentValueWatch.$stateful = true;
|
||||
var unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
|
||||
isolateScope.$on('$destroy', unwatch);
|
||||
break;
|
||||
|
||||
@@ -1721,12 +1799,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
return parentGet(scope, locals);
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
throw $compileMinErr('iscp',
|
||||
"Invalid isolate scope definition for directive '{0}'." +
|
||||
" Definition: {... {1}: '{2}' ...}",
|
||||
newIsolateScopeDirective.name, scopeName, definition);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1788,7 +1860,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (!futureParentElement) {
|
||||
futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
|
||||
}
|
||||
return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement);
|
||||
return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1972,6 +2044,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
boundTranscludeFn = linkQueue.shift(),
|
||||
linkNode = $compileNode[0];
|
||||
|
||||
if (scope.$$destroyed) continue;
|
||||
|
||||
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
|
||||
var oldClasses = beforeTemplateLinkNode.className;
|
||||
|
||||
@@ -1998,6 +2072,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
|
||||
return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
|
||||
var childBoundTranscludeFn = boundTranscludeFn;
|
||||
if (scope.$$destroyed) return;
|
||||
if (linkQueue) {
|
||||
linkQueue.push(scope);
|
||||
linkQueue.push(node);
|
||||
|
||||
+55
-12
@@ -4,6 +4,7 @@
|
||||
*/
|
||||
var nullFormCtrl = {
|
||||
$addControl: noop,
|
||||
$$renameControl: nullFormRenameControl,
|
||||
$removeControl: noop,
|
||||
$setValidity: noop,
|
||||
$$setPending: noop,
|
||||
@@ -14,6 +15,10 @@ var nullFormCtrl = {
|
||||
},
|
||||
SUBMITTED_CLASS = 'ng-submitted';
|
||||
|
||||
function nullFormRenameControl(control, name) {
|
||||
control.$name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ngdoc type
|
||||
* @name form.FormController
|
||||
@@ -51,17 +56,18 @@ SUBMITTED_CLASS = 'ng-submitted';
|
||||
*
|
||||
*/
|
||||
//asks for $scope to fool the BC controller module
|
||||
FormController.$inject = ['$element', '$attrs', '$scope', '$animate'];
|
||||
function FormController(element, attrs, $scope, $animate) {
|
||||
FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
|
||||
function FormController(element, attrs, $scope, $animate, $interpolate) {
|
||||
var form = this,
|
||||
parentForm = element.parent().controller('form') || nullFormCtrl,
|
||||
controls = [];
|
||||
|
||||
var parentForm = form.$$parentForm = element.parent().controller('form') || nullFormCtrl;
|
||||
|
||||
// init state
|
||||
form.$error = {};
|
||||
form.$$success = {};
|
||||
form.$pending = undefined;
|
||||
form.$name = attrs.name || attrs.ngForm;
|
||||
form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
|
||||
form.$dirty = false;
|
||||
form.$pristine = true;
|
||||
form.$valid = true;
|
||||
@@ -70,9 +76,6 @@ function FormController(element, attrs, $scope, $animate) {
|
||||
|
||||
parentForm.$addControl(form);
|
||||
|
||||
// Setup initial state of the control
|
||||
element.addClass(PRISTINE_CLASS);
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name form.FormController#$rollbackViewValue
|
||||
@@ -127,6 +130,17 @@ function FormController(element, attrs, $scope, $animate) {
|
||||
}
|
||||
};
|
||||
|
||||
// Private API: rename a form control
|
||||
form.$$renameControl = function(control, newName) {
|
||||
var oldName = control.$name;
|
||||
|
||||
if (form[oldName] === control) {
|
||||
delete form[oldName];
|
||||
}
|
||||
form[newName] = control;
|
||||
control.$name = newName;
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name form.FormController#$removeControl
|
||||
@@ -230,6 +244,25 @@ function FormController(element, attrs, $scope, $animate) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name form.FormController#$setUntouched
|
||||
*
|
||||
* @description
|
||||
* Sets the form to its untouched state.
|
||||
*
|
||||
* This method can be called to remove the 'ng-touched' class and set the form controls to their
|
||||
* untouched state (ng-untouched class).
|
||||
*
|
||||
* Setting a form controls back to their untouched state is often useful when setting the form
|
||||
* back to its pristine state.
|
||||
*/
|
||||
form.$setUntouched = function () {
|
||||
forEach(controls, function(control) {
|
||||
control.$setUntouched();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name form.FormController#$setSubmitted
|
||||
@@ -415,9 +448,12 @@ var formDirectiveFactory = function(isNgForm) {
|
||||
name: 'form',
|
||||
restrict: isNgForm ? 'EAC' : 'E',
|
||||
controller: FormController,
|
||||
compile: function() {
|
||||
compile: function ngFormCompile(formElement) {
|
||||
// Setup initial state of the control
|
||||
formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
|
||||
|
||||
return {
|
||||
pre: function(scope, formElement, attr, controller) {
|
||||
pre: function ngFormPreLink(scope, formElement, attr, controller) {
|
||||
if (!attr.action) {
|
||||
// we can't use jq events because if a form is destroyed during submission the default
|
||||
// action is not prevented. see #1238
|
||||
@@ -447,13 +483,20 @@ var formDirectiveFactory = function(isNgForm) {
|
||||
});
|
||||
}
|
||||
|
||||
var parentFormCtrl = formElement.parent().controller('form'),
|
||||
alias = attr.name || attr.ngForm;
|
||||
var parentFormCtrl = controller.$$parentForm,
|
||||
alias = controller.$name;
|
||||
|
||||
if (alias) {
|
||||
setter(scope, alias, controller, alias);
|
||||
attr.$observe(attr.name ? 'name' : 'ngForm', function(newValue) {
|
||||
if (alias === newValue) return;
|
||||
setter(scope, alias, undefined, alias);
|
||||
alias = newValue;
|
||||
setter(scope, alias, controller, alias);
|
||||
parentFormCtrl.$$renameControl(controller, alias);
|
||||
});
|
||||
}
|
||||
if (parentFormCtrl) {
|
||||
if (parentFormCtrl !== nullFormCtrl) {
|
||||
formElement.on('$destroy', function() {
|
||||
parentFormCtrl.$removeControl(controller);
|
||||
if (alias) {
|
||||
|
||||
+145
-78
@@ -14,10 +14,10 @@ var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\
|
||||
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
|
||||
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
|
||||
var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
|
||||
var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d))?$/;
|
||||
var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
|
||||
var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
|
||||
var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
|
||||
var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d))?$/;
|
||||
var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
|
||||
var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
|
||||
|
||||
var $ngModelMinErr = new minErr('ngModel');
|
||||
@@ -281,8 +281,8 @@ var inputType = {
|
||||
</example>
|
||||
*/
|
||||
'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
|
||||
createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss']),
|
||||
'yyyy-MM-ddTHH:mm:ss'),
|
||||
createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
|
||||
'yyyy-MM-ddTHH:mm:ss.sss'),
|
||||
|
||||
/**
|
||||
* @ngdoc input
|
||||
@@ -370,8 +370,8 @@ var inputType = {
|
||||
</example>
|
||||
*/
|
||||
'time': createDateInputType('time', TIME_REGEXP,
|
||||
createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss']),
|
||||
'HH:mm:ss'),
|
||||
createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
|
||||
'HH:mm:ss.sss'),
|
||||
|
||||
/**
|
||||
* @ngdoc input
|
||||
@@ -1000,11 +1000,11 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
element.on('change', listener);
|
||||
|
||||
ctrl.$render = function() {
|
||||
element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
|
||||
element.val(ctrl.$isEmpty(ctrl.$modelValue) ? '' : ctrl.$viewValue);
|
||||
};
|
||||
}
|
||||
|
||||
function weekParser(isoWeek) {
|
||||
function weekParser(isoWeek, existingDate) {
|
||||
if (isDate(isoWeek)) {
|
||||
return isoWeek;
|
||||
}
|
||||
@@ -1015,9 +1015,21 @@ function weekParser(isoWeek) {
|
||||
if (parts) {
|
||||
var year = +parts[1],
|
||||
week = +parts[2],
|
||||
hours = 0,
|
||||
minutes = 0,
|
||||
seconds = 0,
|
||||
milliseconds = 0,
|
||||
firstThurs = getFirstThursdayOfYear(year),
|
||||
addDays = (week - 1) * 7;
|
||||
return new Date(year, 0, firstThurs.getDate() + addDays);
|
||||
|
||||
if (existingDate) {
|
||||
hours = existingDate.getHours();
|
||||
minutes = existingDate.getMinutes();
|
||||
seconds = existingDate.getSeconds();
|
||||
milliseconds = existingDate.getMilliseconds();
|
||||
}
|
||||
|
||||
return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1025,7 +1037,7 @@ function weekParser(isoWeek) {
|
||||
}
|
||||
|
||||
function createDateParser(regexp, mapping) {
|
||||
return function(iso) {
|
||||
return function(iso, date) {
|
||||
var parts, map;
|
||||
|
||||
if (isDate(iso)) {
|
||||
@@ -1047,14 +1059,26 @@ function createDateParser(regexp, mapping) {
|
||||
|
||||
if (parts) {
|
||||
parts.shift();
|
||||
map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0 };
|
||||
if (date) {
|
||||
map = {
|
||||
yyyy: date.getFullYear(),
|
||||
MM: date.getMonth() + 1,
|
||||
dd: date.getDate(),
|
||||
HH: date.getHours(),
|
||||
mm: date.getMinutes(),
|
||||
ss: date.getSeconds(),
|
||||
sss: date.getMilliseconds() / 1000
|
||||
};
|
||||
} else {
|
||||
map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
|
||||
}
|
||||
|
||||
forEach(parts, function(part, index) {
|
||||
if (index < mapping.length) {
|
||||
map[mapping[index]] = +part;
|
||||
}
|
||||
});
|
||||
return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0);
|
||||
return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1067,12 +1091,16 @@ function createDateInputType(type, regexp, parseDate, format) {
|
||||
badInputChecker(scope, element, attr, ctrl);
|
||||
baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
||||
var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
|
||||
var previousDate;
|
||||
|
||||
ctrl.$$parserName = type;
|
||||
ctrl.$parsers.push(function(value) {
|
||||
if (ctrl.$isEmpty(value)) return null;
|
||||
if (regexp.test(value)) {
|
||||
var parsedDate = parseDate(value);
|
||||
// Note: We cannot read ctrl.$modelValue, as there might be a different
|
||||
// parser/formatter in the processing chain so that the model
|
||||
// contains some different data format!
|
||||
var parsedDate = parseDate(value, previousDate);
|
||||
if (timezone === 'UTC') {
|
||||
parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset());
|
||||
}
|
||||
@@ -1082,8 +1110,18 @@ function createDateInputType(type, regexp, parseDate, format) {
|
||||
});
|
||||
|
||||
ctrl.$formatters.push(function(value) {
|
||||
if (isDate(value)) {
|
||||
if (!ctrl.$isEmpty(value)) {
|
||||
if (!isDate(value)) {
|
||||
throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
|
||||
}
|
||||
previousDate = value;
|
||||
if (previousDate && timezone === 'UTC') {
|
||||
var timezoneOffset = 60000 * previousDate.getTimezoneOffset();
|
||||
previousDate = new Date(previousDate.getTime() + timezoneOffset);
|
||||
}
|
||||
return $filter('date')(value, format, timezone);
|
||||
} else {
|
||||
previousDate = null;
|
||||
}
|
||||
return '';
|
||||
});
|
||||
@@ -1109,6 +1147,11 @@ function createDateInputType(type, regexp, parseDate, format) {
|
||||
ctrl.$validate();
|
||||
});
|
||||
}
|
||||
// Override the standard $isEmpty to detect invalid dates as well
|
||||
ctrl.$isEmpty = function(value) {
|
||||
// Invalid Date: getTime() returns NaN
|
||||
return !value || (value.getTime && value.getTime() !== value.getTime());
|
||||
};
|
||||
|
||||
function parseObservedDateValue(val) {
|
||||
return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
|
||||
@@ -1192,8 +1235,7 @@ function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
stringBasedInputType(ctrl);
|
||||
|
||||
ctrl.$$parserName = 'url';
|
||||
ctrl.$validators.url = function(modelValue, viewValue) {
|
||||
var value = modelValue || viewValue;
|
||||
ctrl.$validators.url = function(value) {
|
||||
return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
|
||||
};
|
||||
}
|
||||
@@ -1205,8 +1247,7 @@ function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
stringBasedInputType(ctrl);
|
||||
|
||||
ctrl.$$parserName = 'email';
|
||||
ctrl.$validators.email = function(modelValue, viewValue) {
|
||||
var value = modelValue || viewValue;
|
||||
ctrl.$validators.email = function(value) {
|
||||
return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
|
||||
};
|
||||
}
|
||||
@@ -1260,7 +1301,7 @@ function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filt
|
||||
element[0].checked = ctrl.$viewValue;
|
||||
};
|
||||
|
||||
// Override the standard `$isEmpty` because a value of `false` means empty in a checkbox.
|
||||
// Override the standard `$isEmpty` because an empty checkbox is never equal to the trueValue
|
||||
ctrl.$isEmpty = function(value) {
|
||||
return value !== trueValue;
|
||||
};
|
||||
@@ -1425,10 +1466,12 @@ var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
|
||||
return {
|
||||
restrict: 'E',
|
||||
require: ['?ngModel'],
|
||||
link: function(scope, element, attr, ctrls) {
|
||||
if (ctrls[0]) {
|
||||
(inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
|
||||
$browser, $filter, $parse);
|
||||
link: {
|
||||
pre: function(scope, element, attr, ctrls) {
|
||||
if (ctrls[0]) {
|
||||
(inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
|
||||
$browser, $filter, $parse);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1630,8 +1673,8 @@ var VALID_CLASS = 'ng-valid',
|
||||
*
|
||||
*
|
||||
*/
|
||||
var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q',
|
||||
function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q) {
|
||||
var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
|
||||
function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
|
||||
this.$viewValue = Number.NaN;
|
||||
this.$modelValue = Number.NaN;
|
||||
this.$validators = {};
|
||||
@@ -1648,7 +1691,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
this.$error = {}; // keep invalid keys here
|
||||
this.$$success = {}; // keep valid keys here
|
||||
this.$pending = undefined; // keep pending keys here
|
||||
this.$name = $attr.name;
|
||||
this.$name = $interpolate($attr.name || '', false)($scope);
|
||||
|
||||
|
||||
var parsedNgModel = $parse($attr.ngModel),
|
||||
@@ -1719,7 +1762,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
* default. The `checkboxInputType` directive does this because in its case a value of `false`
|
||||
* implies empty.
|
||||
*
|
||||
* @param {*} value Reference to check.
|
||||
* @param {*} value Model value to check.
|
||||
* @returns {boolean} True if `value` is empty.
|
||||
*/
|
||||
this.$isEmpty = function(value) {
|
||||
@@ -1729,11 +1772,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
|
||||
currentValidationRunId = 0;
|
||||
|
||||
// Setup initial state of the control
|
||||
$element
|
||||
.addClass(PRISTINE_CLASS)
|
||||
.addClass(UNTOUCHED_CLASS);
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name ngModel.NgModelController#$setValidity
|
||||
@@ -1906,9 +1944,11 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
|
||||
// check parser error
|
||||
if (!processParseErrors(parseValid)) {
|
||||
validationDone(false);
|
||||
return;
|
||||
}
|
||||
if (!processSyncValidators()) {
|
||||
validationDone(false);
|
||||
return;
|
||||
}
|
||||
processAsyncValidators();
|
||||
@@ -1926,7 +1966,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
forEach(ctrl.$asyncValidators, function(v, name) {
|
||||
setValidity(name, null);
|
||||
});
|
||||
validationDone();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1944,7 +1983,6 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
forEach(ctrl.$asyncValidators, function(v, name) {
|
||||
setValidity(name, null);
|
||||
});
|
||||
validationDone();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1952,6 +1990,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
|
||||
function processAsyncValidators() {
|
||||
var validatorPromises = [];
|
||||
var allValid = true;
|
||||
forEach(ctrl.$asyncValidators, function(validator, name) {
|
||||
var promise = validator(modelValue, viewValue);
|
||||
if (!isPromiseLike(promise)) {
|
||||
@@ -1962,13 +2001,16 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
validatorPromises.push(promise.then(function() {
|
||||
setValidity(name, true);
|
||||
}, function(error) {
|
||||
allValid = false;
|
||||
setValidity(name, false);
|
||||
}));
|
||||
});
|
||||
if (!validatorPromises.length) {
|
||||
validationDone();
|
||||
validationDone(true);
|
||||
} else {
|
||||
$q.all(validatorPromises).then(validationDone);
|
||||
$q.all(validatorPromises).then(function() {
|
||||
validationDone(allValid);
|
||||
}, noop);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1978,10 +2020,10 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
}
|
||||
}
|
||||
|
||||
function validationDone() {
|
||||
function validationDone(allValid) {
|
||||
if (localValidationRunId === currentValidationRunId) {
|
||||
|
||||
doneCallback();
|
||||
doneCallback(allValid);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -2022,14 +2064,17 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
};
|
||||
|
||||
this.$$parseAndValidate = function() {
|
||||
var parserValid = true,
|
||||
viewValue = ctrl.$$lastCommittedViewValue,
|
||||
modelValue = viewValue;
|
||||
for(var i = 0; i < ctrl.$parsers.length; i++) {
|
||||
modelValue = ctrl.$parsers[i](modelValue);
|
||||
if (isUndefined(modelValue)) {
|
||||
parserValid = false;
|
||||
break;
|
||||
var viewValue = ctrl.$$lastCommittedViewValue;
|
||||
var modelValue = viewValue;
|
||||
var parserValid = isUndefined(modelValue) ? undefined : true;
|
||||
|
||||
if (parserValid) {
|
||||
for(var i = 0; i < ctrl.$parsers.length; i++) {
|
||||
modelValue = ctrl.$parsers[i](modelValue);
|
||||
if (isUndefined(modelValue)) {
|
||||
parserValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
|
||||
@@ -2042,9 +2087,13 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
ctrl.$modelValue = modelValue;
|
||||
writeToModelIfNeeded();
|
||||
}
|
||||
ctrl.$$runValidators(parserValid, modelValue, viewValue, function() {
|
||||
ctrl.$$runValidators(parserValid, modelValue, viewValue, function(allValid) {
|
||||
if (!allowInvalid) {
|
||||
ctrl.$modelValue = ctrl.$valid ? modelValue : undefined;
|
||||
// Note: Don't check ctrl.$valid here, as we could have
|
||||
// external validators (e.g. calculated on the server),
|
||||
// that just call $setValidity and need the model value
|
||||
// to calculate their validity.
|
||||
ctrl.$modelValue = allValid ? modelValue : undefined;
|
||||
writeToModelIfNeeded();
|
||||
}
|
||||
});
|
||||
@@ -2342,36 +2391,51 @@ var ngModelDirective = function() {
|
||||
restrict: 'A',
|
||||
require: ['ngModel', '^?form', '^?ngModelOptions'],
|
||||
controller: NgModelController,
|
||||
link: {
|
||||
pre: function(scope, element, attr, ctrls) {
|
||||
var modelCtrl = ctrls[0],
|
||||
formCtrl = ctrls[1] || nullFormCtrl;
|
||||
// Prelink needs to run before any input directive
|
||||
// so that we can set the NgModelOptions in NgModelController
|
||||
// before anyone else uses it.
|
||||
priority: 1,
|
||||
compile: function ngModelCompile(element) {
|
||||
// Setup initial state of the control
|
||||
element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
|
||||
|
||||
modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
|
||||
return {
|
||||
pre: function ngModelPreLink(scope, element, attr, ctrls) {
|
||||
var modelCtrl = ctrls[0],
|
||||
formCtrl = ctrls[1] || nullFormCtrl;
|
||||
|
||||
// notify others, especially parent forms
|
||||
formCtrl.$addControl(modelCtrl);
|
||||
modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
formCtrl.$removeControl(modelCtrl);
|
||||
});
|
||||
},
|
||||
post: function(scope, element, attr, ctrls) {
|
||||
var modelCtrl = ctrls[0];
|
||||
if (modelCtrl.$options && modelCtrl.$options.updateOn) {
|
||||
element.on(modelCtrl.$options.updateOn, function(ev) {
|
||||
modelCtrl.$$debounceViewValueCommit(ev && ev.type);
|
||||
// notify others, especially parent forms
|
||||
formCtrl.$addControl(modelCtrl);
|
||||
|
||||
attr.$observe('name', function(newValue) {
|
||||
if (modelCtrl.$name !== newValue) {
|
||||
formCtrl.$$renameControl(modelCtrl, newValue);
|
||||
}
|
||||
});
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
formCtrl.$removeControl(modelCtrl);
|
||||
});
|
||||
},
|
||||
post: function ngModelPostLink(scope, element, attr, ctrls) {
|
||||
var modelCtrl = ctrls[0];
|
||||
if (modelCtrl.$options && modelCtrl.$options.updateOn) {
|
||||
element.on(modelCtrl.$options.updateOn, function(ev) {
|
||||
modelCtrl.$$debounceViewValueCommit(ev && ev.type);
|
||||
});
|
||||
}
|
||||
|
||||
element.on('blur', function(ev) {
|
||||
if (modelCtrl.$touched) return;
|
||||
|
||||
scope.$apply(function() {
|
||||
modelCtrl.$setTouched();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
element.on('blur', function(ev) {
|
||||
if (modelCtrl.$touched) return;
|
||||
|
||||
scope.$apply(function() {
|
||||
modelCtrl.$setTouched();
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -2463,8 +2527,8 @@ var requiredDirective = function() {
|
||||
if (!ctrl) return;
|
||||
attr.required = true; // force truthy in case we are on non input element
|
||||
|
||||
ctrl.$validators.required = function(modelValue, viewValue) {
|
||||
return !attr.required || !ctrl.$isEmpty(viewValue);
|
||||
ctrl.$validators.required = function(value) {
|
||||
return !attr.required || !ctrl.$isEmpty(value);
|
||||
};
|
||||
|
||||
attr.$observe('required', function() {
|
||||
@@ -2519,7 +2583,7 @@ var maxlengthDirective = function() {
|
||||
ctrl.$validate();
|
||||
});
|
||||
ctrl.$validators.maxlength = function(modelValue, viewValue) {
|
||||
return ctrl.$isEmpty(viewValue) || viewValue.length <= maxlength;
|
||||
return ctrl.$isEmpty(modelValue) || viewValue.length <= maxlength;
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -2538,7 +2602,7 @@ var minlengthDirective = function() {
|
||||
ctrl.$validate();
|
||||
});
|
||||
ctrl.$validators.minlength = function(modelValue, viewValue) {
|
||||
return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
|
||||
return ctrl.$isEmpty(modelValue) || viewValue.length >= minlength;
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -2768,6 +2832,8 @@ var ngValueDirective = function() {
|
||||
* `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
|
||||
* to have access to the updated model.
|
||||
*
|
||||
* `ngModelOptions` has an effect on the element it's declared on and its descendants.
|
||||
*
|
||||
* @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
|
||||
* - `updateOn`: string specifying which event should be the input bound to. You can set several
|
||||
* events using an space delimited list. There is a special event called `default` that
|
||||
@@ -2924,8 +2990,9 @@ function addSetValidityMethod(context) {
|
||||
parentForm = context.parentForm,
|
||||
$animate = context.$animate;
|
||||
|
||||
classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
|
||||
|
||||
ctrl.$setValidity = setValidity;
|
||||
toggleValidationCss('', true);
|
||||
|
||||
function setValidity(validationErrorKey, state, options) {
|
||||
if (state === undefined) {
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*
|
||||
* @element ANY
|
||||
* @scope
|
||||
* @priority 500
|
||||
* @param {expression} ngController Name of a constructor function registered with the current
|
||||
* {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
|
||||
* that on the current scope evaluates to a constructor function.
|
||||
|
||||
+119
-1
@@ -49,7 +49,125 @@
|
||||
...
|
||||
</html>
|
||||
```
|
||||
*/
|
||||
* @example
|
||||
// Note: the suffix `.csp` in the example name triggers
|
||||
// csp mode in our http server!
|
||||
<example name="example.csp" module="cspExample" ng-csp="true">
|
||||
<file name="index.html">
|
||||
<div ng-controller="MainController as ctrl">
|
||||
<div>
|
||||
<button ng-click="ctrl.inc()" id="inc">Increment</button>
|
||||
<span id="counter">
|
||||
{{ctrl.counter}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button ng-click="ctrl.evil()" id="evil">Evil</button>
|
||||
<span id="evilError">
|
||||
{{ctrl.evilError}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('cspExample', [])
|
||||
.controller('MainController', function() {
|
||||
this.counter = 0;
|
||||
this.inc = function() {
|
||||
this.counter++;
|
||||
};
|
||||
this.evil = function() {
|
||||
// jshint evil:true
|
||||
try {
|
||||
eval('1+2');
|
||||
} catch (e) {
|
||||
this.evilError = e.message;
|
||||
}
|
||||
};
|
||||
});
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
var util, webdriver;
|
||||
|
||||
var incBtn = element(by.id('inc'));
|
||||
var counter = element(by.id('counter'));
|
||||
var evilBtn = element(by.id('evil'));
|
||||
var evilError = element(by.id('evilError'));
|
||||
|
||||
function getAndClearSevereErrors() {
|
||||
return browser.manage().logs().get('browser').then(function(browserLog) {
|
||||
return browserLog.filter(function(logEntry) {
|
||||
return logEntry.level.value > webdriver.logging.Level.WARNING.value;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function clearErrors() {
|
||||
getAndClearSevereErrors();
|
||||
}
|
||||
|
||||
function expectNoErrors() {
|
||||
getAndClearSevereErrors().then(function(filteredLog) {
|
||||
expect(filteredLog.length).toEqual(0);
|
||||
if (filteredLog.length) {
|
||||
console.log('browser console errors: ' + util.inspect(filteredLog));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function expectError(regex) {
|
||||
getAndClearSevereErrors().then(function(filteredLog) {
|
||||
var found = false;
|
||||
filteredLog.forEach(function(log) {
|
||||
if (log.message.match(regex)) {
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
if (!found) {
|
||||
throw new Error('expected an error that matches ' + regex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
util = require('util');
|
||||
webdriver = require('protractor/node_modules/selenium-webdriver');
|
||||
});
|
||||
|
||||
// For now, we only test on Chrome,
|
||||
// as Safari does not load the page with Protractor's injected scripts,
|
||||
// and Firefox webdriver always disables content security policy (#6358)
|
||||
if (browser.params.browser !== 'chrome') {
|
||||
return;
|
||||
}
|
||||
|
||||
it('should not report errors when the page is loaded', function() {
|
||||
// clear errors so we are not dependent on previous tests
|
||||
clearErrors();
|
||||
// Need to reload the page as the page is already loaded when
|
||||
// we come here
|
||||
browser.driver.getCurrentUrl().then(function(url) {
|
||||
browser.get(url);
|
||||
});
|
||||
expectNoErrors();
|
||||
});
|
||||
|
||||
it('should evaluate expressions', function() {
|
||||
expect(counter.getText()).toEqual('0');
|
||||
incBtn.click();
|
||||
expect(counter.getText()).toEqual('1');
|
||||
expectNoErrors();
|
||||
});
|
||||
|
||||
it('should throw and report an error when using "eval"', function() {
|
||||
evilBtn.click();
|
||||
expect(evilError.getText()).toMatch(/Content Security Policy/);
|
||||
expectError(/Content Security Policy/);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
|
||||
// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
|
||||
// bootstrap the system (before $parse is instantiated), for this reason we just have
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user