Compare commits
367 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 562c4e424b | |||
| a0ae07bd4e | |||
| adfc322b04 | |||
| 0ef17276e9 | |||
| ac37915ef6 | |||
| 6593d83626 | |||
| 7700024ef5 | |||
| 4899e781ff | |||
| 28af74a901 | |||
| 88335fdfcf | |||
| 72d63dbcc0 | |||
| 055b738d4e | |||
| 2faa4781c5 | |||
| 6f9bcd3307 | |||
| e16e7df689 | |||
| 2972de8a44 | |||
| e0e9ccdb79 | |||
| 3f540e3d8a | |||
| 8b91aa64b7 | |||
| 1c0241e5b2 | |||
| c56e32a7fa | |||
| 8c08fcfb1b | |||
| fbf5ab8f17 | |||
| e395170eef | |||
| 627b0354ec | |||
| 8d18038301 | |||
| c2362e3f45 | |||
| 2b6c2c5fbd | |||
| 0f4016c84a | |||
| c0b4e2db9c | |||
| 924ee6db06 | |||
| cbc7496c1f | |||
| 69d96e8b71 | |||
| b426424e63 | |||
| e1d6178457 | |||
| 3fb2d2ac3b | |||
| e0d4c42148 | |||
| ad4336f935 | |||
| 81147a8bfd | |||
| 82751f5960 | |||
| 8b8992b145 | |||
| 60b2851df7 | |||
| f0e12ea7fe | |||
| 28ef2637c1 | |||
| 8d38ec3892 | |||
| 8e404c4dc1 | |||
| 7570e9f07d | |||
| d90f83cda8 | |||
| fcc3a7a423 | |||
| e3814b1266 | |||
| 0d691a6feb | |||
| 2022fd768e | |||
| e37e67eadb | |||
| af72f40a55 | |||
| 7a543c985e | |||
| 7d1719e219 | |||
| accd35b747 | |||
| e16c6aa31d | |||
| 305696c660 | |||
| ff03d7b323 | |||
| 498835a1c4 | |||
| 6ac6621661 | |||
| 39335d96d3 | |||
| 737ef25df1 | |||
| e19dbc7793 | |||
| 06d0f1aea6 | |||
| eda00ce23f | |||
| 8377e81827 | |||
| 828ad89e8a | |||
| bdec35cebc | |||
| f0da1a3615 | |||
| 950c9724ba | |||
| e0375a61d0 | |||
| 8681276590 | |||
| 92e8289c12 | |||
| 219648fada | |||
| 6ab7adda3c | |||
| 7bcaf39437 | |||
| 2da94a701b | |||
| 5ce17efbbd | |||
| b1559be5cf | |||
| 8570585692 | |||
| 9178c318e1 | |||
| 546cb429d9 | |||
| 1db3b8cfb7 | |||
| 0ebfa0d112 | |||
| 88c2193c71 | |||
| 21f9316338 | |||
| 8d0cb30688 | |||
| 45c356586b | |||
| 02fcac5e04 | |||
| 64a3f42f14 | |||
| ff428e7283 | |||
| a990078173 | |||
| f0407d2aa0 | |||
| 49e7c32bb4 | |||
| 3f047704c7 | |||
| b2e48e61c7 | |||
| d5a2069cd5 | |||
| 1192531e9b | |||
| 5dee9e4a33 | |||
| b10a4371a6 | |||
| ede9984b36 | |||
| 613a5cc5db | |||
| 4b1695ec61 | |||
| 24a045c3b2 | |||
| 61a8e198bd | |||
| 66091756b8 | |||
| 6743ccf788 | |||
| 2ad7bb9ca9 | |||
| 3878be52f6 | |||
| acfcbdf906 | |||
| 879b0bc9f9 | |||
| faec99794d | |||
| 66a132847d | |||
| 2354924a46 | |||
| 940fcb4090 | |||
| b389cfc49e | |||
| 1d9ac65d37 | |||
| f45241649b | |||
| a8d42800e7 | |||
| 091eb83301 | |||
| 28613f0eae | |||
| 64d404612f | |||
| 6c7fdd845b | |||
| b517f49a80 | |||
| abddefd057 | |||
| 4f38ba9898 | |||
| f7cf680d23 | |||
| e101c127af | |||
| 3d31a15cc8 | |||
| 4ae5f7a477 | |||
| d845d8a742 | |||
| 461d6990cf | |||
| ef64169db3 | |||
| 8b0b7ca65a | |||
| 20b22f1f7e | |||
| 708f2ba984 | |||
| dbe381f29f | |||
| e55c8bcbca | |||
| 28453015fc | |||
| 6b7a1b82bc | |||
| 7b0c5b937c | |||
| a526ae8f77 | |||
| b63fd11800 | |||
| c369563818 | |||
| 8e2c62ae9d | |||
| cb6b976851 | |||
| 7227f1a479 | |||
| 1cb8584e84 | |||
| c67bd69c58 | |||
| 71c11e96c6 | |||
| c9677920d4 | |||
| 83e36db85d | |||
| d64d41ed99 | |||
| ba48797bb0 | |||
| f9eb324716 | |||
| 862e587b46 | |||
| 6cd6ec62fd | |||
| 549166740b | |||
| 2e3a972b32 | |||
| ddb8081982 | |||
| ccfa72dfa1 | |||
| 7914d3463b | |||
| 10110bc3f7 | |||
| 0ed0207dfb | |||
| 6621adb6bb | |||
| 789328de9b | |||
| 14a2142484 | |||
| 2a4f92ee99 | |||
| 47384bc61b | |||
| 50eb3b2bd6 | |||
| 876df04606 | |||
| 908ab52b8d | |||
| ff5cf736e5 | |||
| f552f25171 | |||
| ec8e3957d2 | |||
| d0df8c8946 | |||
| b1d1cb6b7a | |||
| 1d2414ca93 | |||
| 9f62d9d20b | |||
| 08354ae1f7 | |||
| 3f9f1ad502 | |||
| a2aa667777 | |||
| 0619e6f278 | |||
| 545c62ab18 | |||
| e584111335 | |||
| 5d9eccc2d4 | |||
| 7a7e9f4047 | |||
| 15859cf2c4 | |||
| 489d0d46d7 | |||
| 33e6e0519c | |||
| aa249ae4a2 | |||
| d6d7fe4b07 | |||
| 47ba601460 | |||
| 1bebe36aa9 | |||
| 2317af6851 | |||
| 3bf8d6c612 | |||
| 372e31ae84 | |||
| 6bf3a12eec | |||
| 273e34e3de | |||
| 98d5885237 | |||
| af042b60db | |||
| ca1d126005 | |||
| f6877ed2d9 | |||
| d6bcbc773c | |||
| 9d07796227 | |||
| 664e680948 | |||
| abd6889dca | |||
| 074648ef57 | |||
| fa844f64cb | |||
| 008fbe53d1 | |||
| ae342b5ce7 | |||
| 562334f5f1 | |||
| 080ac5a262 | |||
| e14d1a7988 | |||
| 726ffdc50f | |||
| b93ca855c0 | |||
| e307e2ab89 | |||
| 0779b6bfc0 | |||
| 0b02e53ca5 | |||
| eeda289f0e | |||
| 6e34da67cd | |||
| a2acd794b3 | |||
| 3982d9bcb1 | |||
| ccba305ee5 | |||
| 3f609f9952 | |||
| 76dbb6e395 | |||
| 4a6d4de53e | |||
| b472d0275f | |||
| 8339c2ebff | |||
| 34a10c6ace | |||
| 635cdaaa9a | |||
| c7a49b34c6 | |||
| 408d9583b8 | |||
| 3f2d756532 | |||
| 6011145cfe | |||
| b26fc23b06 | |||
| 0c930a1a86 | |||
| 2f61b2f045 | |||
| 4d4da556eb | |||
| 58f5da8664 | |||
| fb6062fb9d | |||
| 0c65f1ae3e | |||
| f40f54c6da | |||
| 37bc5ef4d8 | |||
| 3652831084 | |||
| bc42950b51 | |||
| 6680b7b97c | |||
| c839f78b8f | |||
| f7ce415c67 | |||
| 4cf2adfeda | |||
| 2b84f43a6d | |||
| d6cfcacee1 | |||
| 299b220f5e | |||
| 78057a945e | |||
| c5e41a0325 | |||
| 748a6c8d9d | |||
| ed4cd6c3c6 | |||
| 4cc00e7aed | |||
| f7b36844e6 | |||
| 959297c38a | |||
| f8f97f8b61 | |||
| 2ca6d650e8 | |||
| 547871e779 | |||
| a4b70cfd71 | |||
| c9fbb472b7 | |||
| df6d34c52b | |||
| ed22869e08 | |||
| ee07b502a2 | |||
| 63ec18f5c9 | |||
| e381c4dd09 | |||
| 68e84acec9 | |||
| e118a8be34 | |||
| 9202767f41 | |||
| 7fb88698dc | |||
| bdcc657c7e | |||
| c995b09b77 | |||
| 8a96f317e5 | |||
| d3aa14bc11 | |||
| cd49876e34 | |||
| 55a0bc453c | |||
| 2daaf3ea19 | |||
| e1484cdf65 | |||
| d0781eb1a3 | |||
| d09056d287 | |||
| 849e4472e1 | |||
| f55278fa8a | |||
| df5624147f | |||
| 91e6d1d22f | |||
| b8cc71d476 | |||
| 511422adb0 | |||
| dd3587a8c1 | |||
| e5dd832b20 | |||
| a15d9cb4b6 | |||
| 9bfbb16e23 | |||
| 2b741dc8b8 | |||
| e888dde3c5 | |||
| d5294ebfa0 | |||
| 6d6ebf7c61 | |||
| 44b940e88d | |||
| 56e73ea355 | |||
| bfb6af7053 | |||
| d7be9588a0 | |||
| 53e4da8eab | |||
| 7b5e019981 | |||
| 129e2e021a | |||
| 3cc02e7f03 | |||
| 79592ce9e2 | |||
| a9a38d84b9 | |||
| 24a67f9515 | |||
| 91ef3a31a0 | |||
| cea44b3e86 | |||
| e8c6b9bf25 | |||
| 5412372e93 | |||
| 4f823f902d | |||
| fe0e434a87 | |||
| edad4e63df | |||
| f684cb09a5 | |||
| d7717d93e4 | |||
| 247ec19c82 | |||
| 78165c224d | |||
| d1214af132 | |||
| 11c5bb7f3d | |||
| d6419d0aff | |||
| 55848a9139 | |||
| 0f13f24ad2 | |||
| 0d8de2d3ea | |||
| 7833ce0a6e | |||
| 47ab8df455 | |||
| b700282ffd | |||
| 1b9395ea8f | |||
| 44d160e3ce | |||
| 4f90c9b531 | |||
| 11aceac273 | |||
| f08bf6f1f7 | |||
| ca4ddfadba | |||
| 4bab3d8227 | |||
| b12c6b485d | |||
| 9c353b4f17 | |||
| 21243d62a2 | |||
| ad309b1332 | |||
| 7a75356388 | |||
| dc57fe97e1 | |||
| 853999de10 | |||
| 53ec5e13e5 | |||
| 235731d32b | |||
| 5af8d2963b | |||
| 0b4a41af58 | |||
| 0e066693f2 | |||
| 02cc2b2014 | |||
| 486f1b4e51 | |||
| c5f2f583ab | |||
| 186a68f8ff | |||
| 46bd6dc88d | |||
| 0609453e1f | |||
| 7682e5747a | |||
| eaa1d00b24 | |||
| 3cf2da0e38 | |||
| 9335378602 | |||
| de2ecb8a96 | |||
| 66fdc03642 | |||
| 8e2e9adb46 | |||
| 7d604975a7 | |||
| 02075dcf13 | |||
| 7c73bc916e | |||
| 2036fb1e71 |
@@ -1,21 +0,0 @@
|
||||
# http://editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[src/ngLocale/**]
|
||||
insert_final_newline = false
|
||||
|
||||
[dropdown-toggle.js]
|
||||
trim_trailing_whitespace = false
|
||||
insert_final_newline = false
|
||||
|
||||
[htmlparser.js]
|
||||
insert_final_newline = false
|
||||
+2
-2
@@ -9,10 +9,10 @@ performance/temp*.html
|
||||
*.swp
|
||||
angular.js.tmproj
|
||||
/node_modules/
|
||||
bower_components/
|
||||
/components/
|
||||
/bower_components/
|
||||
angular.xcodeproj
|
||||
.idea
|
||||
*.iml
|
||||
.agignore
|
||||
libpeerconnection.log
|
||||
npm-debug.log
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
node_modules/**
|
||||
lib/htmlparser/**
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"bitwise": true,
|
||||
"immed": true,
|
||||
"newcap": true,
|
||||
"noarg": true,
|
||||
"noempty": true,
|
||||
"nonew": true,
|
||||
"trailing": true,
|
||||
"maxlen": 200,
|
||||
"boss": true,
|
||||
"eqnull": true,
|
||||
"expr": true,
|
||||
"globalstrict": true,
|
||||
"laxbreak": true,
|
||||
"loopfunc": true,
|
||||
"sub": true,
|
||||
"undef": true,
|
||||
"indent": 2
|
||||
}
|
||||
+4
-8
@@ -1,6 +1,6 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- '0.10'
|
||||
- 0.10
|
||||
|
||||
branches:
|
||||
except:
|
||||
@@ -11,6 +11,7 @@ env:
|
||||
- JOB=unit
|
||||
- JOB=e2e TEST_TARGET=jqlite
|
||||
- JOB=e2e TEST_TARGET=jquery
|
||||
- JOB=e2e TEST_TARGET=doce2e
|
||||
global:
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
@@ -18,13 +19,8 @@ env:
|
||||
- BROWSER_PROVIDER_READY_FILE=/tmp/sauce-connect-ready
|
||||
|
||||
install:
|
||||
# - npm config set registry http://23.251.144.68
|
||||
# Disable the spinner, it looks bad on Travis
|
||||
- npm config set spin false
|
||||
# Log HTTP requests
|
||||
- npm config set loglevel http
|
||||
- time ./scripts/travis/npm-bundle-deps.sh
|
||||
- time npm install
|
||||
- npm config set registry http://23.251.144.68
|
||||
- npm install
|
||||
|
||||
before_script:
|
||||
- mkdir -p $LOGS_DIR
|
||||
|
||||
+319
-2657
File diff suppressed because it is too large
Load Diff
+12
-11
@@ -40,7 +40,7 @@ would like to implement a new feature then consider what kind of change it is:
|
||||
[dev mailing list][angular-dev] or [IRC][irc] so that we can better coordinate our efforts, prevent
|
||||
duplication of work, and help you to craft the change so that it is successfully accepted into the
|
||||
project.
|
||||
* **Small Changes** can be crafted and submitted to [GitHub Repository][github] as a Pull Request.
|
||||
* **Small Changes** can be crafted and submitted to the [GitHub Repository][github] as a Pull Request.
|
||||
|
||||
|
||||
## <a name="docs"></a> Want a Doc Fix?
|
||||
@@ -65,13 +65,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)
|
||||
|
||||
@@ -84,7 +84,7 @@ Before you submit your pull request consider the following guidelines:
|
||||
|
||||
* Search [GitHub](https://github.com/angular/angular.js/pulls) for an open or closed Pull Request
|
||||
that relates to your submission. You don't want to duplicate effort.
|
||||
* Please sign our [Contributor License Agreement (CLA)](#signing-the-cla) before sending pull
|
||||
* Please sign our [Contributor License Agreement (CLA)](#cla) before sending pull
|
||||
requests. We cannot accept code without this.
|
||||
* Make your changes in a new git branch
|
||||
|
||||
@@ -93,7 +93,7 @@ Before you submit your pull request consider the following guidelines:
|
||||
```
|
||||
|
||||
* Create your patch, **including appropriate test cases**.
|
||||
* Follow our [Coding Rules](#coding-rules).
|
||||
* Follow our [Coding Rules](#rules).
|
||||
* Run the full Angular test suite, as described in the [developer documentation][dev-doc],
|
||||
and ensure that all tests pass.
|
||||
* Commit your changes using a descriptive commit message that follows our
|
||||
@@ -119,7 +119,7 @@ Before you submit your pull request consider the following guidelines:
|
||||
```
|
||||
|
||||
* In GitHub, send a pull request to `angular:master`.
|
||||
* If we suggest changes then
|
||||
* If we suggest changes then
|
||||
* Make the required updates.
|
||||
* Re-run the Angular test suite to ensure tests are still passing.
|
||||
* Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
@@ -172,7 +172,7 @@ To ensure consistency throughout the source code, keep these rules in mind as yo
|
||||
* **Do not use namespaces**: Instead, wrap the entire angular code base in an anonymous closure and
|
||||
export our API explicitly rather than implicitly.
|
||||
* Wrap all code at **100 characters**.
|
||||
* Instead of complex inheritance hierarchies, we **prefer simple objects**. We use prototypal
|
||||
* Instead of complex inheritance hierarchies, we **prefer simple objects**. We use prototypical
|
||||
inheritance only when absolutely necessary.
|
||||
* We **love functions and closures** and, whenever possible, prefer them over objects.
|
||||
* To write concise code that can be better minified, we **use aliases internally** that map to the
|
||||
@@ -237,7 +237,7 @@ reference GitHub issues that this commit **Closes**.
|
||||
|
||||
A detailed explanation can be found in this [document][commit-message-format].
|
||||
|
||||
## <a name="cla"></a> Signing the CLA
|
||||
## <a name="cla"></a> Signing the CLA
|
||||
|
||||
Please sign our Contributor License Agreement (CLA) before sending pull requests. For any code
|
||||
changes to be accepted, the CLA must be signed. It's a quick process, we promise!
|
||||
@@ -252,13 +252,14 @@ You can find out more detailed information about contributing in the
|
||||
|
||||
|
||||
|
||||
[Google Closure I18N library]: https://github.com/google/closure-library/tree/master/closure/goog/i18n
|
||||
[Google Closure I18N library]: https://code.google.com/p/closure-library/source/browse/closure/goog/i18n/
|
||||
[angular-dev]: https://groups.google.com/forum/?fromgroups#!forum/angular-dev
|
||||
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[contribute]: http://docs.angularjs.org/misc/contribute
|
||||
[contributing]: http://docs.angularjs.org/misc/contribute
|
||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
[dev-doc]: https://docs.angularjs.org/guide
|
||||
[github]: https://github.com/angular/angular.js
|
||||
[groups]: https://groups.google.com/forum/?fromgroups#!forum/angular
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
@@ -269,6 +270,6 @@ You can find out more detailed information about contributing in the
|
||||
[ngDocs]: https://github.com/angular/angular.js/wiki/Writing-AngularJS-Documentation
|
||||
[plunker]: http://plnkr.co/edit
|
||||
[stackoverflow]: http://stackoverflow.com/questions/tagged/angularjs
|
||||
[unit-testing]: https://docs.angularjs.org/guide/unit-testing
|
||||
[unit-testing]: http://docs.angularjs.org/guide/dev_guide.unit-testing
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
+9
-14
@@ -1,5 +1,3 @@
|
||||
'use strict';
|
||||
|
||||
var files = require('./angularFiles').files;
|
||||
var util = require('./lib/grunt/utils.js');
|
||||
var versionInfo = require('./lib/versions/version-info');
|
||||
@@ -109,9 +107,6 @@ module.exports = function(grunt) {
|
||||
options: {
|
||||
jshintrc: true,
|
||||
},
|
||||
node: {
|
||||
files: { src: ['*.js', 'lib/**/*.js'] },
|
||||
},
|
||||
tests: {
|
||||
files: { src: 'test/**/*.js' },
|
||||
},
|
||||
@@ -127,6 +122,9 @@ module.exports = function(grunt) {
|
||||
ngLocale: {
|
||||
files: { src: 'src/ngLocale/**/*.js' },
|
||||
},
|
||||
ngMessages: {
|
||||
files: { src: 'src/ngMessages/**/*.js' },
|
||||
},
|
||||
ngMock: {
|
||||
files: { src: 'src/ngMock/**/*.js' },
|
||||
},
|
||||
@@ -195,6 +193,10 @@ module.exports = function(grunt) {
|
||||
dest: 'build/angular-resource.js',
|
||||
src: util.wrap(files['angularModules']['ngResource'], 'module')
|
||||
},
|
||||
messages: {
|
||||
dest: 'build/angular-messages.js',
|
||||
src: util.wrap(files['angularModules']['ngMessages'], 'module')
|
||||
},
|
||||
animate: {
|
||||
dest: 'build/angular-animate.js',
|
||||
src: util.wrap(files['angularModules']['ngAnimate'], 'module')
|
||||
@@ -228,11 +230,8 @@ module.exports = function(grunt) {
|
||||
|
||||
"ddescribe-iit": {
|
||||
files: [
|
||||
'src/**/*.js',
|
||||
'test/**/*.js',
|
||||
'!test/ngScenario/DescribeSpec.js',
|
||||
'!src/ng/directive/booleanAttrs.js', // legitimate xit here
|
||||
'!src/ngScenario/**/*.js'
|
||||
'!test/ngScenario/DescribeSpec.js'
|
||||
]
|
||||
},
|
||||
|
||||
@@ -257,11 +256,7 @@ module.exports = function(grunt) {
|
||||
compress: {
|
||||
build: {
|
||||
options: {archive: 'build/' + dist +'.zip', mode: 'zip'},
|
||||
src: ['**'],
|
||||
cwd: 'build',
|
||||
expand: true,
|
||||
dot: true,
|
||||
dest: dist + '/'
|
||||
src: ['**'], cwd: 'build', expand: true, dot: true, dest: dist + '/'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -6,17 +6,16 @@ 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 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!
|
||||
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!
|
||||
|
||||
* Web site: http://angularjs.org
|
||||
* Tutorial: http://docs.angularjs.org/tutorial
|
||||
* API Docs: http://docs.angularjs.org/api
|
||||
* Developer Guide: http://docs.angularjs.org/guide
|
||||
* Contribution guidelines: [CONTRIBUTING.md](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md)
|
||||
* Contribution guidelines: http://docs.angularjs.org/misc/contribute
|
||||
* Dashboard: http://dashboard.angularjs.org
|
||||
|
||||
Building AngularJS
|
||||
@@ -38,7 +37,7 @@ To execute end-to-end (e2e) tests, use:
|
||||
grunt test:e2e
|
||||
|
||||
To learn more about the grunt tasks, run `grunt --help` and also read our
|
||||
[contribution guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md).
|
||||
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
+9
-6
@@ -26,6 +26,7 @@ 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?
|
||||
@@ -33,8 +34,9 @@ This process based on the idea of minimizing user pain
|
||||
* Check if there are comments that link to a dupe. If so verify that this is indeed a dupe, [close it][], and go to the last step.
|
||||
1. Bugs:
|
||||
* 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? - Steps to reproduce the bug are clear. If they are not,
|
||||
* 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.
|
||||
@@ -42,11 +44,11 @@ This process based on the idea of minimizing user pain
|
||||
* Label `needs: breaking change` - if needed
|
||||
* Label `needs: public api` - if the issue requires introduction of a new public API
|
||||
1. Label `browser: *` - if the issue **only** affects a certain browser
|
||||
1. Label `frequency: *` – How often does this issue come up? How many developers does this affect? Chose just one of the following:
|
||||
1. Label `frequency: *` – How often does this issue come up? How many developers does this affect?
|
||||
* low - obscure issue affecting a handful of developers
|
||||
* moderate - impacts a common usage pattern
|
||||
* high - impacts most or all Angular apps
|
||||
1. Label `severity: *` - How bad is the issue? Chose just one of the following:
|
||||
1. Label `severity: *` - How bad is the issue?
|
||||
* security issue
|
||||
* regression
|
||||
* memory leak
|
||||
@@ -57,10 +59,11 @@ 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
|
||||
|
||||
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
|
||||
|
||||
|
||||
1. Unassign yourself from the issue
|
||||
|
||||
|
||||
Vendored
+6
-3
@@ -1,6 +1,4 @@
|
||||
'use strict';
|
||||
|
||||
var angularFiles = {
|
||||
angularFiles = {
|
||||
'angularSrc': [
|
||||
'src/minErr.js',
|
||||
'src/Angular.js',
|
||||
@@ -82,6 +80,9 @@ var angularFiles = {
|
||||
'ngCookies': [
|
||||
'src/ngCookies/cookies.js'
|
||||
],
|
||||
'ngMessages': [
|
||||
'src/ngMessages/messages.js'
|
||||
],
|
||||
'ngResource': [
|
||||
'src/ngResource/resource.js'
|
||||
],
|
||||
@@ -130,6 +131,7 @@ var angularFiles = {
|
||||
'test/auto/*.js',
|
||||
'test/ng/**/*.js',
|
||||
'test/ngAnimate/*.js',
|
||||
'test/ngMessages/*.js',
|
||||
'test/ngCookies/*.js',
|
||||
'test/ngResource/*.js',
|
||||
'test/ngRoute/**/*.js',
|
||||
@@ -191,6 +193,7 @@ var angularFiles = {
|
||||
|
||||
angularFiles['angularSrcModules'] = [].concat(
|
||||
angularFiles['angularModules']['ngAnimate'],
|
||||
angularFiles['angularModules']['ngMessages'],
|
||||
angularFiles['angularModules']['ngCookies'],
|
||||
angularFiles['angularModules']['ngResource'],
|
||||
angularFiles['angularModules']['ngRoute'],
|
||||
|
||||
+5
-1
@@ -2,7 +2,11 @@
|
||||
"name": "AngularJS",
|
||||
"devDependencies": {
|
||||
"jquery": "1.10.2",
|
||||
"lunr.js": "0.4.3",
|
||||
"open-sans-fontface": "1.0.4",
|
||||
"google-code-prettify": "1.0.1",
|
||||
"closure-compiler": "https://closure-compiler.googlecode.com/files/compiler-20130603.zip",
|
||||
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip"
|
||||
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip",
|
||||
"bootstrap": "3.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
+3
-6
@@ -3,8 +3,6 @@
|
||||
// TODO(vojta): pre-commit hook for validating messages
|
||||
// TODO(vojta): report errors, currently Q silence everything which really sucks
|
||||
|
||||
'use strict';
|
||||
|
||||
var child = require('child_process');
|
||||
var fs = require('fs');
|
||||
var util = require('util');
|
||||
@@ -112,7 +110,7 @@ var printSection = function(stream, title, section, printCommitLinks) {
|
||||
}
|
||||
stream.write(')\n');
|
||||
} else {
|
||||
stream.write(util.format('%s %s\n', prefix, commit.subject));
|
||||
stream.write(util.format('%s %s', prefix, commit.subject));
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -166,7 +164,7 @@ var writeChangelog = function(stream, commits, version) {
|
||||
hash: commit.hash,
|
||||
closes: []
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
stream.write(util.format(HEADER_TPL, version, version, currentDate()));
|
||||
@@ -174,7 +172,7 @@ var writeChangelog = function(stream, commits, version) {
|
||||
printSection(stream, 'Features', sections.feat);
|
||||
printSection(stream, 'Performance Improvements', sections.perf);
|
||||
printSection(stream, 'Breaking Changes', sections.breaks, false);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var getPreviousTag = function() {
|
||||
@@ -188,7 +186,6 @@ var getPreviousTag = function() {
|
||||
|
||||
|
||||
var generate = function(version, file) {
|
||||
|
||||
getPreviousTag().then(function(tag) {
|
||||
console.log('Reading git log since', tag);
|
||||
readGitLog('^fix|^feat|^perf|BREAKING', tag).then(function(commits) {
|
||||
|
||||
+1
-5
@@ -1,7 +1,3 @@
|
||||
/* global describe: false, it: false, expect: false */
|
||||
|
||||
'use strict';
|
||||
|
||||
describe('changelog.js', function() {
|
||||
var ch = require('./changelog');
|
||||
|
||||
@@ -17,7 +13,7 @@ describe('changelog.js', function() {
|
||||
expect(msg.hash).toBe('9b1aff905b638aa274a5fc8f88662df446d374bd');
|
||||
expect(msg.subject).toBe('broadcast $destroy event on scope destruction');
|
||||
expect(msg.body).toBe('perf testing shows that in chrome this change adds 5-15% overhead\n' +
|
||||
'when destroying 10k nested scopes where each scope has a $destroy listener\n');
|
||||
'when destroying 10k nested scopes where each scope has a $destroy listener\n')
|
||||
expect(msg.component).toBe('scope');
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
#!/usr/local/bin/node
|
||||
|
||||
'use strict';
|
||||
#!/usr/bin/env node
|
||||
|
||||
var util = require('util');
|
||||
var cp = require('child_process');
|
||||
@@ -123,9 +121,12 @@ then(function (tags) {
|
||||
value();
|
||||
}).
|
||||
then(function (tags) {
|
||||
var master = tags.pop();
|
||||
var stable = tags.pop();
|
||||
|
||||
return [
|
||||
{ name: 'v1.0.x', tag: tags[0] },
|
||||
{ name: 'master', tag: tags[1] }
|
||||
{ name: stable.replace(/\d+$/, 'x'), tag: stable },
|
||||
{ name: 'master', tag: master}
|
||||
];
|
||||
}).
|
||||
then(allInSeries(function (branch) {
|
||||
@@ -145,7 +146,7 @@ then(allInSeries(function (branch) {
|
||||
return sha + (msg.toLowerCase().indexOf('fix') === -1 ? ' ' : ' * ') + msg;
|
||||
});
|
||||
branch.log = log.map(function (line) {
|
||||
return line.substr(41);
|
||||
return line.substr(41)
|
||||
});
|
||||
return branch;
|
||||
});
|
||||
|
||||
@@ -9,14 +9,3 @@
|
||||
ng\:form {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ng-animate-block-transitions {
|
||||
transition:0s all!important;
|
||||
-webkit-transition:0s all!important;
|
||||
}
|
||||
|
||||
/* show the element during a show/hide animation when the
|
||||
* animation is ongoing, but the .ng-hide class is active */
|
||||
.ng-hide-add-active, .ng-hide-remove {
|
||||
display: block!important;
|
||||
}
|
||||
|
||||
@@ -211,10 +211,6 @@ code.highlighted {
|
||||
color:maroon;
|
||||
}
|
||||
|
||||
ul + p {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.docs-version-jump {
|
||||
min-width:100%;
|
||||
max-width:100%;
|
||||
@@ -257,9 +253,6 @@ ul + p {
|
||||
z-index: 99;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
-moz-appearance: none;
|
||||
text-indent: 0.01px;
|
||||
text-overflow: '';
|
||||
}
|
||||
|
||||
.picker:after {
|
||||
@@ -316,10 +309,10 @@ iframe.example {
|
||||
}
|
||||
|
||||
.search-results-group.col-group-api { width:30%; }
|
||||
.search-results-group.col-group-guide,
|
||||
.search-results-group.col-group-tutorial { width:20%; }
|
||||
.search-results-group.col-group-guide { width:30%; }
|
||||
.search-results-group.col-group-tutorial { width:25%; }
|
||||
.search-results-group.col-group-misc,
|
||||
.search-results-group.col-group-error { width:15%; float: right; }
|
||||
.search-results-group.col-group-error { float:right; clear:both; width:15% }
|
||||
|
||||
|
||||
.search-results-group.col-group-api .search-result {
|
||||
@@ -391,6 +384,7 @@ iframe.example {
|
||||
position:fixed;
|
||||
top:120px;
|
||||
bottom:0;
|
||||
padding-bottom:120px;
|
||||
overflow:auto;
|
||||
}
|
||||
|
||||
@@ -411,7 +405,6 @@ iframe.example {
|
||||
|
||||
.main-body-grid .side-navigation {
|
||||
position:relative;
|
||||
padding-bottom:120px;
|
||||
}
|
||||
|
||||
.main-body-grid .side-navigation.ng-hide {
|
||||
@@ -579,15 +572,6 @@ ul.events > li {
|
||||
margin-bottom:40px;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 769px) and (max-width: 991px) {
|
||||
.main-body-grid {
|
||||
margin-top: 160px;
|
||||
}
|
||||
.main-body-grid > .grid-left {
|
||||
top: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width : 768px) {
|
||||
.picker, .picker select {
|
||||
width:auto;
|
||||
@@ -635,14 +619,12 @@ ul.events > li {
|
||||
display:inline-block;
|
||||
padding:3px 0;
|
||||
}
|
||||
.nav-index-group .nav-index-listing:not(.nav-index-section):after {
|
||||
padding-right:5px;
|
||||
margin-left:-3px;
|
||||
content:", ";
|
||||
.nav-index-group .nav-index-listing:not(.nav-index-section) + .nav-index-listing:not(.nav-index-section):after {
|
||||
padding-right:5px;
|
||||
content:", ";
|
||||
}
|
||||
.nav-index-group .nav-index-listing:last-child:after {
|
||||
.nav-index-group .nav-index-listing:last-child {
|
||||
content:"";
|
||||
display:inline-block;
|
||||
}
|
||||
.nav-index-group .nav-index-section {
|
||||
display:block;
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.lang-text * {
|
||||
color: #333333!important;
|
||||
}
|
||||
|
||||
.pln {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ pre.prettyprint.linenums {
|
||||
}
|
||||
ol.linenums {
|
||||
margin: 0 0 0 33px; /* IE indents via margin-left */
|
||||
}
|
||||
}
|
||||
ol.linenums li {
|
||||
padding-left: 12px;
|
||||
font-size:12px;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 40 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 141 KiB |
@@ -0,0 +1,284 @@
|
||||
'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);
|
||||
+7
-7
@@ -11,7 +11,7 @@ directive.runnableExample = ['$templateCache', '$document', function($templateCa
|
||||
'ng-repeat="tab in tabs track by $index" ' +
|
||||
'href="" ' +
|
||||
'class="btn"' +
|
||||
'ng-click="setTab($index)">' +
|
||||
'ng-click="setTab($index)">' +
|
||||
' {{ tab }}' +
|
||||
' </a>' +
|
||||
'</nav>';
|
||||
@@ -103,7 +103,7 @@ directive.syntax = function() {
|
||||
restrict: 'A',
|
||||
link: function(scope, element, attrs) {
|
||||
function makeLink(type, text, link, icon) {
|
||||
return '<a href="' + link + '" class="btn syntax-' + type + '" target="_blank" rel="nofollow">' +
|
||||
return '<a href="' + link + '" class="btn syntax-' + type + '" target="_blank" rel="nofollow">' +
|
||||
'<span class="' + icon + '"></span> ' + text +
|
||||
'</a>';
|
||||
};
|
||||
@@ -274,13 +274,13 @@ var popoverElement = function() {
|
||||
this.contentElement = angular.element(inner.childNodes[1]);
|
||||
|
||||
//stop the click on the tooltip
|
||||
this.element.bind('click', function(event) {
|
||||
this.element.on('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
});
|
||||
|
||||
var self = this;
|
||||
angular.element(document.body).bind('click',function(event) {
|
||||
angular.element(document.body).on('click',function(event) {
|
||||
if(self.visible()) self.hide();
|
||||
});
|
||||
},
|
||||
@@ -307,7 +307,7 @@ var popoverElement = function() {
|
||||
return this.titleElement.html(value);
|
||||
},
|
||||
|
||||
content : function(value) {
|
||||
content : function(value) {
|
||||
if(value && value.length > 0) {
|
||||
value = marked(value);
|
||||
}
|
||||
@@ -359,7 +359,7 @@ directive.popover = ['popoverElement', function(popover) {
|
||||
restrict: 'A',
|
||||
priority : 500,
|
||||
link: function(scope, element, attrs) {
|
||||
element.bind('click',function(event) {
|
||||
element.on('click',function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if(popover.isSituatedAt(element) && popover.visible()) {
|
||||
@@ -396,7 +396,7 @@ directive.foldout = ['$http', '$animate','$window', function($http, $animate, $w
|
||||
if(/\/build\//.test($window.location.href)) {
|
||||
url = '/build/docs' + url;
|
||||
}
|
||||
element.bind('click',function() {
|
||||
element.on('click',function() {
|
||||
scope.$apply(function() {
|
||||
if(!container) {
|
||||
if(loading) return;
|
||||
|
||||
+5
-5
@@ -35,8 +35,8 @@ angular.module('ui.bootstrap.dropdown', [])
|
||||
|
||||
this.open = function( dropdownScope ) {
|
||||
if ( !openScope ) {
|
||||
$document.bind('click', closeDropdown);
|
||||
$document.bind('keydown', escapeKeyBind);
|
||||
$document.on('click', closeDropdown);
|
||||
$document.on('keydown', escapeKeyBind);
|
||||
}
|
||||
|
||||
if ( openScope && openScope !== dropdownScope ) {
|
||||
@@ -49,8 +49,8 @@ angular.module('ui.bootstrap.dropdown', [])
|
||||
this.close = function( dropdownScope ) {
|
||||
if ( openScope === dropdownScope ) {
|
||||
openScope = null;
|
||||
$document.unbind('click', closeDropdown);
|
||||
$document.unbind('keydown', escapeKeyBind);
|
||||
$document.off('click', closeDropdown);
|
||||
$document.off('keydown', escapeKeyBind);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -124,7 +124,7 @@ angular.module('ui.bootstrap.dropdown', [])
|
||||
return;
|
||||
}
|
||||
|
||||
element.bind('click', function(event) {
|
||||
element.on('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
"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 });
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
'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+$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
'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/);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
'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)');
|
||||
});
|
||||
|
||||
});
|
||||
+3
-3
@@ -6,7 +6,6 @@ angular.module('docsApp', [
|
||||
'DocsController',
|
||||
'versionsData',
|
||||
'pagesData',
|
||||
'navData',
|
||||
'directives',
|
||||
'errors',
|
||||
'examples',
|
||||
@@ -14,10 +13,11 @@ angular.module('docsApp', [
|
||||
'tutorials',
|
||||
'versions',
|
||||
'bootstrap',
|
||||
'bootstrapPrettify',
|
||||
'ui.bootstrap.dropdown'
|
||||
])
|
||||
|
||||
|
||||
.config(['$locationProvider', function($locationProvider) {
|
||||
.config(function($locationProvider) {
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
}]);
|
||||
});
|
||||
@@ -22,7 +22,7 @@ angular.module('directives', [])
|
||||
terminal: true,
|
||||
compile: function(element) {
|
||||
var linenums = element.hasClass('linenum');// || element.parent()[0].nodeName === 'PRE';
|
||||
var match = /lang-(\S)+/.exec(element.className);
|
||||
var match = /lang-(\S+)/.exec(element[0].className);
|
||||
var lang = match && match[1];
|
||||
var html = element.html();
|
||||
element.html(window.prettyPrintOne(html, lang, linenums));
|
||||
|
||||
+74
-9
@@ -6,10 +6,31 @@ 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.path,
|
||||
@@ -17,22 +38,55 @@ angular.module('DocsController', [])
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
$scope.$on('$includeContentLoaded', function() {
|
||||
$scope.afterPartialLoaded = 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) {
|
||||
|
||||
path = path.replace(/^\/?(.+?)(\/index)?\/?$/, '$1');
|
||||
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);
|
||||
}
|
||||
|
||||
currentPage = $scope.currentPage = NG_PAGES[path];
|
||||
|
||||
if ( currentPage ) {
|
||||
$scope.partialPath = 'partials/' + path + '.html';
|
||||
$scope.currentArea = NG_NAVIGATION[currentPage.area];
|
||||
$scope.currentArea = currentPage && NG_NAVIGATION[currentPage.area];
|
||||
var pathParts = currentPage.path.split('/');
|
||||
var breadcrumb = $scope.breadcrumb = [];
|
||||
var breadcrumbPath = '';
|
||||
@@ -44,7 +98,6 @@ angular.module('DocsController', [])
|
||||
} else {
|
||||
$scope.currentArea = NG_NAVIGATION['api'];
|
||||
$scope.breadcrumb = [];
|
||||
$scope.partialPath = 'Error404.html';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -54,12 +107,24 @@ 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;
|
||||
});
|
||||
}
|
||||
});
|
||||
}]);
|
||||
|
||||
@@ -59,4 +59,4 @@ angular.module('errors', ['ngSanitize'])
|
||||
element.html(errorLinkFilter(interpolate.apply(null, formatArgs), '_blank'));
|
||||
}
|
||||
};
|
||||
}]);
|
||||
}]);
|
||||
@@ -2,12 +2,7 @@ angular.module('examples', [])
|
||||
|
||||
.factory('formPostData', ['$document', function($document) {
|
||||
return function(url, fields) {
|
||||
/**
|
||||
* Form previously posted to target="_blank", but pop-up blockers were causing this to not work.
|
||||
* If a user chose to bypass pop-up blocker one time and click the link, they would arrive at
|
||||
* a new default plnkr, not a plnkr with the desired template.
|
||||
*/
|
||||
var form = angular.element('<form style="display: none;" method="post" action="' + url + '"></form>');
|
||||
var form = angular.element('<form style="display: none;" method="post" action="' + url + '" target="_blank"></form>');
|
||||
angular.forEach(fields, function(value, name) {
|
||||
var input = angular.element('<input type="hidden" name="' + name + '">');
|
||||
input.attr('value', value);
|
||||
@@ -74,4 +69,4 @@ angular.module('examples', [])
|
||||
formPostData('http://plnkr.co/edit/?p=preview', postData);
|
||||
});
|
||||
};
|
||||
}]);
|
||||
}]);
|
||||
@@ -0,0 +1,24 @@
|
||||
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;
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
+72
-133
@@ -8,37 +8,24 @@ angular.module('search', [])
|
||||
}
|
||||
|
||||
$scope.search = function(q) {
|
||||
var MIN_SEARCH_LENGTH = 2;
|
||||
var MIN_SEARCH_LENGTH = 3;
|
||||
if(q.length >= MIN_SEARCH_LENGTH) {
|
||||
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;
|
||||
});
|
||||
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;
|
||||
}
|
||||
else {
|
||||
clearResults();
|
||||
}
|
||||
if(!$scope.$$phase) $scope.$apply();
|
||||
};
|
||||
|
||||
$scope.submit = function() {
|
||||
var result;
|
||||
for(var i in $scope.results) {
|
||||
@@ -48,129 +35,81 @@ angular.module('search', [])
|
||||
}
|
||||
}
|
||||
if(result) {
|
||||
$location.path(result.path);
|
||||
$location.path(result.url);
|
||||
$scope.hideResults();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.hideResults = function() {
|
||||
clearResults();
|
||||
$scope.q = '';
|
||||
};
|
||||
}])
|
||||
|
||||
|
||||
.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;
|
||||
});
|
||||
});
|
||||
.controller('Error404SearchCtrl', ['$scope', '$location', 'docsSearch', function($scope, $location, docsSearch) {
|
||||
$scope.results = docsSearch($location.path().split(/[\/\.:]/).pop());
|
||||
}])
|
||||
|
||||
.factory('lunrSearch', function() {
|
||||
return function(properties) {
|
||||
if (window.RUNNING_IN_NG_TEST_RUNNER) return null;
|
||||
|
||||
.provider('docsSearch', function() {
|
||||
|
||||
// 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) {
|
||||
|
||||
console.log('Using Local Search Index');
|
||||
|
||||
// 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;
|
||||
});
|
||||
var engine = lunr(properties);
|
||||
return {
|
||||
store : function(values) {
|
||||
engine.add(values);
|
||||
},
|
||||
search : function(q) {
|
||||
return engine.search(q);
|
||||
}
|
||||
};
|
||||
}
|
||||
localSearchFactory.$inject = ['$http', '$timeout', 'NG_PAGES'];
|
||||
|
||||
// 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;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 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
|
||||
};
|
||||
})
|
||||
|
||||
.factory('docsSearch', ['$rootScope','lunrSearch', 'NG_PAGES',
|
||||
function($rootScope, lunrSearch, NG_PAGES) {
|
||||
if (window.RUNNING_IN_NG_TEST_RUNNER) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var index = lunrSearch(function() {
|
||||
this.ref('id');
|
||||
this.field('title', {boost: 50});
|
||||
this.field('keywords', { boost : 20 });
|
||||
});
|
||||
|
||||
angular.forEach(NG_PAGES, function(page, key) {
|
||||
if(page.searchTerms) {
|
||||
index.store({
|
||||
id : key,
|
||||
title : page.searchTerms.titleWords,
|
||||
keywords : page.searchTerms.keywords
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
return function(q) {
|
||||
var results = {
|
||||
api : [],
|
||||
tutorial : [],
|
||||
guide : [],
|
||||
error : [],
|
||||
misc : []
|
||||
};
|
||||
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;
|
||||
};
|
||||
}])
|
||||
|
||||
.directive('focused', function($timeout) {
|
||||
return function(scope, element, attrs) {
|
||||
element[0].focus();
|
||||
@@ -192,7 +131,7 @@ angular.module('search', [])
|
||||
return function(scope, element, attrs) {
|
||||
var ESCAPE_KEY_KEYCODE = 27,
|
||||
FORWARD_SLASH_KEYCODE = 191;
|
||||
angular.element($document[0].body).bind('keydown', function(event) {
|
||||
angular.element($document[0].body).on('keydown', function(event) {
|
||||
var input = element[0];
|
||||
if(event.keyCode == FORWARD_SLASH_KEYCODE && document.activeElement != input) {
|
||||
event.stopPropagation();
|
||||
@@ -201,7 +140,7 @@ angular.module('search', [])
|
||||
}
|
||||
});
|
||||
|
||||
element.bind('keydown', function(event) {
|
||||
element.on('keydown', function(event) {
|
||||
if(event.keyCode == ESCAPE_KEY_KEYCODE) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
+16
-15
@@ -1,6 +1,6 @@
|
||||
angular.module('tutorials', [])
|
||||
|
||||
.directive('docTutorialNav', function() {
|
||||
.directive('docTutorialNav', function(templateMerge) {
|
||||
var pages = [
|
||||
'',
|
||||
'step_00', 'step_01', 'step_02', 'step_03', 'step_04',
|
||||
@@ -8,22 +8,23 @@ angular.module('tutorials', [])
|
||||
'step_10', 'step_11', 'step_12', 'the_end'
|
||||
];
|
||||
return {
|
||||
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;
|
||||
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
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
};
|
||||
})
|
||||
@@ -46,4 +47,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,29 +1,15 @@
|
||||
angular.module('versions', [])
|
||||
|
||||
.controller('DocsVersionsCtrl', ['$scope', '$location', '$window', 'NG_VERSIONS', function($scope, $location, $window, NG_VERSIONS) {
|
||||
$scope.docs_versions = NG_VERSIONS;
|
||||
$scope.docs_version = NG_VERSIONS[0];
|
||||
|
||||
for(var i=0, minor = NaN; i < NG_VERSIONS.length; i++) {
|
||||
var version = NG_VERSIONS[i];
|
||||
// NaN will give false here
|
||||
if (minor <= version.minor) {
|
||||
continue;
|
||||
}
|
||||
version.isLatest = true;
|
||||
minor = version.minor;
|
||||
}
|
||||
|
||||
$scope.docs_versions = NG_VERSIONS;
|
||||
$scope.getGroupName = function(v) {
|
||||
return v.isLatest ? 'Latest' : (v.isStable ? 'Stable' : 'Unstable');
|
||||
};
|
||||
|
||||
$scope.jumpToDocsVersion = function(version) {
|
||||
var currentPagePath = $location.path().replace(/\/$/, '');
|
||||
var currentPagePath = $location.path();
|
||||
|
||||
// TODO: We need to do some munging of the path for different versions of the API...
|
||||
|
||||
|
||||
|
||||
$window.location = version.docsUrl + currentPagePath;
|
||||
};
|
||||
}]);
|
||||
}]);
|
||||
@@ -0,0 +1,38 @@
|
||||
describe("code", function() {
|
||||
var prettyPrintOne, oldPP;
|
||||
var compile, scope;
|
||||
|
||||
var any = jasmine.any;
|
||||
|
||||
beforeEach(module('directives'));
|
||||
|
||||
beforeEach(inject(function($rootScope, $compile) {
|
||||
// Provide stub for pretty print function
|
||||
oldPP = window.prettyPrintOne;
|
||||
prettyPrintOne = window.prettyPrintOne = jasmine.createSpy();
|
||||
|
||||
scope = $rootScope.$new();
|
||||
compile = $compile;
|
||||
}));
|
||||
|
||||
afterEach(function() {
|
||||
window.prettyPrintOne = oldPP;
|
||||
});
|
||||
|
||||
|
||||
it('should pretty print innerHTML', function() {
|
||||
compile('<code>var x;</code>')(scope);
|
||||
expect(prettyPrintOne).toHaveBeenCalledWith('var x;', null, false);
|
||||
});
|
||||
|
||||
it('should allow language declaration', function() {
|
||||
compile('<code class="lang-javascript"></code>')(scope);
|
||||
expect(prettyPrintOne).toHaveBeenCalledWith(any(String), 'javascript', false);
|
||||
});
|
||||
|
||||
it('supports allow line numbers', function() {
|
||||
compile('<code class="linenum"></code>')(scope);
|
||||
expect(prettyPrintOne).toHaveBeenCalledWith(any(String), null, true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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.$broadcast('$includeContentLoaded');
|
||||
$scope.afterPartialLoaded();
|
||||
expect($window._gaq.pop()).toEqual(['_trackPageview', 'a/b/c']);
|
||||
}));
|
||||
|
||||
@@ -27,8 +27,8 @@ 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.$broadcast('$includeContentLoaded');
|
||||
$scope.afterPartialLoaded();
|
||||
expect($window._gaq.pop()).toEqual(['_trackPageview', 'x/y/z']);
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "AngularJS-docs-app",
|
||||
"dependencies": {
|
||||
"jquery": "2.1.1",
|
||||
"lunr.js": "0.4.3",
|
||||
"open-sans-fontface": "1.0.4",
|
||||
"google-code-prettify": "1.0.1",
|
||||
"bootstrap": "3.1.1"
|
||||
}
|
||||
}
|
||||
+32
-155
@@ -1,166 +1,43 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require('lodash');
|
||||
var path = require('canonical-path');
|
||||
var packagePath = __dirname;
|
||||
|
||||
var Package = require('dgeni').Package;
|
||||
var basePackage = require('dgeni-packages/ngdoc');
|
||||
var examplesPackage = require('dgeni-packages/examples');
|
||||
|
||||
// Create and export a new Dgeni package called angularjs. This package depends upon
|
||||
// the ngdoc,nunjucks and examples 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')
|
||||
])
|
||||
module.exports = function(config) {
|
||||
|
||||
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')
|
||||
]);
|
||||
|
||||
.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')
|
||||
]);
|
||||
|
||||
|
||||
.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'],
|
||||
pathTemplate: '.',
|
||||
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(checkAnchorLinksProcessor) {
|
||||
checkAnchorLinksProcessor.base = '/';
|
||||
// We are only interested in docs that have an area (i.e. they are pages)
|
||||
checkAnchorLinksProcessor.checkDoc = function(doc) { return doc.area; };
|
||||
})
|
||||
|
||||
|
||||
.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
|
||||
];
|
||||
});
|
||||
return config;
|
||||
};
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
"use strict";
|
||||
|
||||
var typeClassFilter = require('dgeni-packages/ngdoc/rendering/filters/type-class');
|
||||
var encoder = new require('node-html-encoder').Encoder();
|
||||
|
||||
/**
|
||||
* @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>';
|
||||
}
|
||||
};
|
||||
};
|
||||
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>';
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
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,52 +1,59 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require('lodash');
|
||||
var log = require('winston');
|
||||
var path = require('canonical-path');
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
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) {
|
||||
|
||||
// Create error namespace docs and attach error docs to each
|
||||
docs.forEach(function(doc) {
|
||||
var parts, namespaceDoc;
|
||||
// Create error namespace docs and attach error docs to each
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( doc.docType === 'error' ) {
|
||||
|
||||
if ( doc.docType === 'error' ) {
|
||||
// Parse out the error info from the id
|
||||
parts = doc.name.split(':');
|
||||
doc.namespace = parts[0];
|
||||
doc.name = parts[1];
|
||||
|
||||
// Parse out the error info from the id
|
||||
parts = doc.name.split(':');
|
||||
doc.namespace = parts[0];
|
||||
doc.name = parts[1];
|
||||
|
||||
// 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];
|
||||
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'
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
errorNamespaceMap.forEach(function(errorNamespace) {
|
||||
docs.push(errorNamespace);
|
||||
});
|
||||
}
|
||||
};
|
||||
// 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));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
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,43 +1,40 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require('lodash');
|
||||
var log = require('winston');
|
||||
var path = require('canonical-path');
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
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) {
|
||||
|
||||
// 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);
|
||||
});
|
||||
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 = {};
|
||||
_.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,64 +1,50 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require('lodash');
|
||||
var log = require('winston');
|
||||
var fs = require('fs');
|
||||
var path = require('canonical-path');
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
module.exports = {
|
||||
name: 'keywords',
|
||||
runAfter: ['docs-processed'],
|
||||
runBefore: ['adding-extra-docs'],
|
||||
description: 'This processor extracts all the keywords from the document',
|
||||
process: function(docs, config) {
|
||||
|
||||
// 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 ( this.ignoreWordsFile ) {
|
||||
// Load up the keywords to ignore, if specified in the config
|
||||
if ( config.processing.search && config.processing.search.ignoreWordsFile ) {
|
||||
|
||||
var ignoreWordsPath = path.resolve(readFilesProcessor.basePath, this.ignoreWordsFile);
|
||||
wordsToIgnore = fs.readFileSync(ignoreWordsPath, 'utf8').toString().split(/[,\s\n\r]+/gm);
|
||||
var ignoreWordsPath = path.resolve(config.basePath, config.processing.search.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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
function extractWords(text, words, keywordMap) {
|
||||
|
||||
@@ -66,49 +52,37 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
|
||||
_.forEach(tokens, function(token){
|
||||
var match = token.match(KEYWORD_REGEX);
|
||||
if (match){
|
||||
var key = match[1];
|
||||
key = match[1];
|
||||
if ( !keywordMap[key]) {
|
||||
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);
|
||||
var members = [];
|
||||
var membersMap = {};
|
||||
|
||||
// Search each top level property of the document for search terms
|
||||
_.forEach(doc, function(value, key) {
|
||||
|
||||
if ( _.isString(value) && !propertiesToIgnore[key] ) {
|
||||
extractWords(value, words, keywordMap);
|
||||
}
|
||||
|
||||
if ( key === 'methods' || key === 'properties' || key === 'events' ) {
|
||||
_.forEach(value, function(member) {
|
||||
extractWords(member.name, members, membersMap);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
doc.searchTerms = {
|
||||
titleWords: extractTitleWords(doc.name),
|
||||
keywords: _.sortBy(words).join(' '),
|
||||
members: _.sortBy(members).join(' ')
|
||||
keywords: _.sortBy(words).join(' ')
|
||||
};
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -1,7 +1,6 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require('lodash');
|
||||
var path = require('canonical-path');
|
||||
var log = require('winston');
|
||||
|
||||
var AREA_NAMES = {
|
||||
api: 'API',
|
||||
@@ -34,206 +33,182 @@ function getNavGroup(pages, area, pageSorter, pageMapper) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
var navGroupMappers = {
|
||||
api: function(areaPages, area) {
|
||||
var navGroups = _(areaPages)
|
||||
.filter('module') // We are not interested in docs that are not in a module
|
||||
|
||||
.groupBy('module')
|
||||
|
||||
var navGroupMappers = {
|
||||
api: function(areaPages, area) {
|
||||
var navGroups = _(areaPages)
|
||||
.filter('module') // We are not interested in docs that are not in a module
|
||||
.map(function(modulePages, moduleName) {
|
||||
log.debug('moduleName: ' + moduleName);
|
||||
var navItems = [];
|
||||
var modulePage;
|
||||
|
||||
.groupBy('module')
|
||||
_(modulePages)
|
||||
|
||||
.map(function(modulePages, moduleName) {
|
||||
log.debug('moduleName: ' + moduleName);
|
||||
var navItems = [];
|
||||
var modulePage;
|
||||
.groupBy('docType')
|
||||
|
||||
_(modulePages)
|
||||
.tap(function(docTypes) {
|
||||
log.debug(_.keys(docTypes));
|
||||
// Extract the module page from the collection
|
||||
modulePage = docTypes.module[0];
|
||||
delete docTypes.module;
|
||||
})
|
||||
|
||||
.groupBy('docType')
|
||||
.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;
|
||||
}
|
||||
})
|
||||
|
||||
.tap(function(docTypes) {
|
||||
log.debug(moduleName, _.keys(docTypes));
|
||||
// Extract the module page from the collection
|
||||
modulePage = docTypes.module[0];
|
||||
delete docTypes.module;
|
||||
})
|
||||
.forEach(function(sectionPages, sectionName) {
|
||||
|
||||
.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;
|
||||
}
|
||||
})
|
||||
sectionPages = _.sortBy(sectionPages, 'name');
|
||||
|
||||
.forEach(function(sectionPages, sectionName) {
|
||||
if ( sectionPages.length > 0 ) {
|
||||
// Push a navItem for this section
|
||||
navItems.push({
|
||||
name: sectionName,
|
||||
type: 'section',
|
||||
href: path.dirname(sectionPages[0].path)
|
||||
});
|
||||
|
||||
sectionPages = _.sortBy(sectionPages, 'name');
|
||||
// Push the rest of the sectionPages for this section
|
||||
_.forEach(sectionPages, function(sectionPage) {
|
||||
|
||||
if ( sectionPages.length > 0 ) {
|
||||
// Push a navItem for this section
|
||||
navItems.push({
|
||||
name: sectionName,
|
||||
type: 'section',
|
||||
href: path.dirname(sectionPages[0].path)
|
||||
name: sectionPage.name,
|
||||
href: sectionPage.path,
|
||||
type: sectionPage.docType
|
||||
});
|
||||
|
||||
// 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: page.name,
|
||||
step: page.step,
|
||||
href: page.path,
|
||||
type: 'tutorial'
|
||||
name: moduleName,
|
||||
href: modulePage.path,
|
||||
type: 'group',
|
||||
navItems: navItems
|
||||
};
|
||||
})];
|
||||
},
|
||||
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) {
|
||||
return {
|
||||
name: page.name,
|
||||
href: page.path,
|
||||
type: 'page'
|
||||
};
|
||||
}
|
||||
)];
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
$runAfter: ['paths-computed', 'generateKeywordsProcessor'],
|
||||
$runBefore: ['rendering-docs'],
|
||||
$process: function(docs) {
|
||||
|
||||
// We are only interested in docs that are in an area
|
||||
var pages = _.filter(docs, function(doc) {
|
||||
return doc.area;
|
||||
});
|
||||
|
||||
// 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 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
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
.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, '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) {
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
// 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();
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
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
|
||||
};
|
||||
|
||||
if (env === 'jquery') {
|
||||
protractorDoc.examplePath = example.outputFolder + '/index-jquery.html';
|
||||
} else {
|
||||
protractorDoc.examplePath = example.outputFolder + '/index.html';
|
||||
}
|
||||
return protractorDoc;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
};
|
||||
@@ -1,34 +1,38 @@
|
||||
"use strict";
|
||||
|
||||
var _ = require('lodash');
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
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) {
|
||||
|
||||
var versionDoc = {
|
||||
docType: 'versions-data',
|
||||
id: 'versions-data',
|
||||
template: 'versions-data.template.js',
|
||||
outputPath: 'js/versions-data.js',
|
||||
currentVersion: gitData.version
|
||||
};
|
||||
var version = gitData.version;
|
||||
var versions = gitData.versions;
|
||||
|
||||
versionDoc.versions = _(gitData.versions)
|
||||
.filter(function(version) { return version.major > 0; })
|
||||
.push(gitData.version)
|
||||
.reverse()
|
||||
.value();
|
||||
|
||||
docs.push(versionDoc);
|
||||
if ( !version ) {
|
||||
throw new Error('Invalid configuration. Please provide a valid `source.currentVersion` property');
|
||||
}
|
||||
};
|
||||
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);
|
||||
}
|
||||
};
|
||||
@@ -1,39 +0,0 @@
|
||||
"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'
|
||||
]
|
||||
};
|
||||
};
|
||||
@@ -1,39 +0,0 @@
|
||||
"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
@@ -1,43 +0,0 @@
|
||||
"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'
|
||||
]
|
||||
};
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
"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'
|
||||
]
|
||||
};
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
"use strict";
|
||||
var StringMap = require('stringmap');
|
||||
|
||||
/**
|
||||
* @dgService errorNamespaceMap
|
||||
* A map of error namespaces by name.
|
||||
*/
|
||||
module.exports = function errorNamespaceMap() {
|
||||
return new StringMap();
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
"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);
|
||||
};
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
"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;
|
||||
};
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
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,6 +0,0 @@
|
||||
module.exports = {
|
||||
name: 'sortOrder',
|
||||
transforms: function(doc, tag, value) {
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
};
|
||||
@@ -14,4 +14,4 @@
|
||||
{$ doc.description | marked $}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -25,4 +25,4 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -56,6 +56,15 @@
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
// 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']);
|
||||
@@ -76,7 +85,7 @@
|
||||
<div class="row">
|
||||
<div class="col-md-9 header-branding">
|
||||
<a class="brand navbar-brand" href="http://angularjs.org">
|
||||
<img width="117" height="30" class="logo" ng-src="img/angularjs-for-header-only.svg">
|
||||
<img class="logo" src="img/angularjs-for-header-only.svg">
|
||||
</a>
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="divider-vertical"></li>
|
||||
@@ -166,7 +175,7 @@
|
||||
<div class="container main-grid main-header-grid">
|
||||
<div class="grid-left">
|
||||
<div ng-controller="DocsVersionsCtrl" class="picker version-picker">
|
||||
<select ng-options="v as ('v' + v.version + (v.isSnapshot ? ' (snapshot)' : '')) group by getGroupName(v) for v in docs_versions"
|
||||
<select ng-options="v as ('v' + v.version + (v.isSnapshot ? ' (snapshot)' : '')) group by (v.isStable?'Stable':'Unstable') for v in docs_versions"
|
||||
ng-model="docs_version"
|
||||
ng-change="jumpToDocsVersion(docs_version)"
|
||||
class="docs-version-jump">
|
||||
@@ -210,7 +219,7 @@
|
||||
</div>
|
||||
<div class="grid-right">
|
||||
<div id="loading" ng-show="loading">Loading...</div>
|
||||
<div ng-hide="loading" ng-include="partialPath" autoscroll></div>
|
||||
<div ng-hide="loading" ng-include="currentPage.outputPath || 'Error404.html'" onload="afterPartialLoaded()" autoscroll></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{$ doc.data | json $}
|
||||
@@ -1,3 +0,0 @@
|
||||
// Meta data used by the AngularJS docs app
|
||||
angular.module('navData', [])
|
||||
.value('NG_NAVIGATION', {$ doc.areas | json $});
|
||||
@@ -1,3 +1,4 @@
|
||||
// Meta data used by the AngularJS docs app
|
||||
angular.module('pagesData', [])
|
||||
.value('NG_PAGES', {$ doc.pages | json $});
|
||||
.value('NG_PAGES', {$ doc.pages | json $})
|
||||
.value('NG_NAVIGATION', {$ doc.areas | json $});
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
describe("{$ doc.description $}", function() {
|
||||
beforeEach(function() {
|
||||
browser.get("{$ doc.pathPrefix $}/{$ doc.examplePath $}");
|
||||
});
|
||||
|
||||
{$ doc.innerTest $}
|
||||
});
|
||||
@@ -2,16 +2,15 @@
|
||||
is HTML and wrap each line in a <p> - thus breaking the HTML #}
|
||||
|
||||
<div>
|
||||
<a ng-click="openPlunkr('{$ doc.path $}')" class="btn pull-right">
|
||||
<a ng-click="openPlunkr('{$ doc.example.outputFolder $}')" class="btn pull-right">
|
||||
<i class="glyphicon glyphicon-edit"> </i>
|
||||
Edit in Plunker</a>
|
||||
|
||||
<div class="runnable-example"
|
||||
path="{$ doc.example.deployments.default.path $}"
|
||||
path="{$ doc.example.outputFolder $}"
|
||||
{%- 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 -%}
|
||||
@@ -20,9 +19,9 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<iframe class="runnable-example-frame" src="{$ doc.example.deployments.default.outputPath $}" name="{$ doc.example.id $}"></iframe>
|
||||
<iframe class="runnable-example-frame" src="{$ doc.example.outputFolder $}/index.html" name="{$ doc.example.id $}"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Be aware that we need these extra new lines here or marked will not realise that the <div>
|
||||
above is HTML and wrap each line in a <p> - thus breaking the HTML #}
|
||||
above is HTML and wrap each line in a <p> - thus breaking the HTML #}
|
||||
@@ -1 +1 @@
|
||||
{% include 'overview.template.html' %}
|
||||
{% include 'overview.template.html' %}
|
||||
@@ -6,7 +6,7 @@
|
||||
Welcome to the AngularJS API docs page. These pages contain the AngularJS reference materials for version <strong ng-bind="version"></strong>.
|
||||
|
||||
The documentation is organized into **{@link guide/module modules}** which contain various components of an AngularJS application.
|
||||
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.
|
||||
These components are {@link guide/directive directives}, {@link guide/services services}, {@link guide/filter filters}, {@link guide/providers providers}, {@link guide/templates types}, global APIs and testing mocks.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Angular Namespaces `$` and `$$`**
|
||||
@@ -212,7 +212,7 @@ Use ngTouch when developing for mobile browsers/devices.
|
||||
{@link ngTouch#service Services / Factories}
|
||||
</td>
|
||||
<td>
|
||||
The {@link ngTouch.$swipe $swipe} service is used to register and manage mobile DOM events.
|
||||
The {@link ngTouch.$swipe $swipe} service is used to register and manage mobile DOM events.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -252,7 +252,7 @@ Use ngSanitize to securely parse and manipulate HTML data in your application.
|
||||
|
||||
## {@link ngMock ngMock}
|
||||
|
||||
Use ngMock to inject and mock modules, factories, services and providers within your unit tests
|
||||
Use ngMock to inject and mock modules, factories, services and providers within your unit tests
|
||||
|
||||
<div class="alert alert-info">Include the **angular-mocks.js** file into your test runner for this to work.</div>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ myModule.directive('myDirective', function factory() {
|
||||
return {
|
||||
...
|
||||
scope: {
|
||||
localValue: '=bind'
|
||||
'bind': '=localValue'
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
@@ -37,17 +37,3 @@ elements. For example:
|
||||
```
|
||||
<b>Hello</b> World!
|
||||
```
|
||||
|
||||
Watch out for html comments at the beginning or end of templates, as these can cause this error as
|
||||
well. Consider the following template:
|
||||
|
||||
```
|
||||
<div class='container'>
|
||||
<div class='wrapper>
|
||||
...
|
||||
</div> <!-- wrapper -->
|
||||
</div> <!-- container -->
|
||||
```
|
||||
|
||||
The `<!-- container -->` comment is interpreted as a second root element and causes the template to
|
||||
be invalid.
|
||||
|
||||
@@ -12,10 +12,10 @@ $controller(MyController);
|
||||
$controller(MyController, {scope: newScope});
|
||||
```
|
||||
|
||||
To fix the example above please provide a scope (using the `$scope` property in the locals object) to the $controller call:
|
||||
To fix the example above please provide a scope 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.
|
||||
|
||||
@@ -23,4 +23,4 @@ When an instance of `MyCtrl` is created, the service `myService` will be created
|
||||
by the `$injector`. `myService` depends on itself, which causes the `$injector`
|
||||
to detect a circular dependency and throw the error.
|
||||
|
||||
For more information, see the {@link guide/di Dependency Injection Guide}.
|
||||
For more information, see the {@link guide/di Dependency Injection Guide}.
|
||||
@@ -23,4 +23,4 @@ To avoid the error, always use string literals for dependency injection annotati
|
||||
tokens.
|
||||
|
||||
For an explanation of what injection annotations are and how to use them, refer
|
||||
to the {@link guide/di Dependency Injection Guide}.
|
||||
to the {@link guide/di Dependency Injection Guide}.
|
||||
@@ -23,4 +23,4 @@ angular.module("myApp", [])
|
||||
```
|
||||
|
||||
For more information, refer to the {@link auto.$provide#provider
|
||||
$provide.provider} api doc.
|
||||
$provide.provider} api doc.
|
||||
@@ -0,0 +1,54 @@
|
||||
@ngdoc error
|
||||
@name $injector:strictdi
|
||||
@fullName Explicit annotation required
|
||||
@description
|
||||
|
||||
This error occurs when attempting to invoke a function or provider which
|
||||
has not been explicitly annotated, while the application is running with
|
||||
strict-di mode enabled.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
angular.module("myApp", [])
|
||||
// BadController cannot be invoked, because
|
||||
// the dependencies to be injected are not
|
||||
// explicitly listed.
|
||||
.controller("BadController", function($scope, $http, $filter) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
To fix the error, explicitly annotate the function using either the inline
|
||||
bracket notation, or with the $inject property:
|
||||
|
||||
```
|
||||
function GoodController1($scope, $http, $filter) {
|
||||
// ...
|
||||
}
|
||||
GoodController1.$inject = ["$scope", "$http", "$filter"];
|
||||
|
||||
angular.module("myApp", [])
|
||||
// GoodController1 can be invoked because it
|
||||
// had an $inject property, which is an array
|
||||
// containing the dependency names to be
|
||||
// injected.
|
||||
.controller("GoodController1", GoodController1)
|
||||
|
||||
// GoodController2 can also be invoked, because
|
||||
// the dependencies to inject are listed, in
|
||||
// order, in the array, with the function to be
|
||||
// invoked trailing on the end.
|
||||
.controller("GoodController2", [
|
||||
"$scope",
|
||||
"$http",
|
||||
"$filter",
|
||||
function($scope, $http, $filter) {
|
||||
// ...
|
||||
}
|
||||
]);
|
||||
|
||||
```
|
||||
|
||||
For more information about strict-di mode, see {@link ng.directive:ngApp ngApp}
|
||||
and {@link api/angular.bootstrap angular.bootstrap}.
|
||||
@@ -14,9 +14,8 @@ angular.module('myApp', [])
|
||||
}]);
|
||||
```
|
||||
|
||||
The above code will fail with `$injector:unpr` if `myService` is not defined.
|
||||
|
||||
Making sure each dependency is defined will fix the problem, as noted below.
|
||||
This code will fail with `$injector:unpr` if `myService` is not defined. Making
|
||||
sure each dependency is defined will fix the problem.
|
||||
|
||||
```
|
||||
angular.module('myApp', [])
|
||||
@@ -24,48 +23,4 @@ angular.module('myApp', [])
|
||||
.controller('MyController', ['myService', function (myService) {
|
||||
// Do something with myService
|
||||
}]);
|
||||
```
|
||||
|
||||
An unknown provider error can also be caused by accidentally redefining a
|
||||
module using the `angular.module` API, as shown in the following example.
|
||||
|
||||
```
|
||||
angular.module('myModule', [])
|
||||
.service('myCoolService', function () { /* ... */ });
|
||||
|
||||
angular.module('myModule', [])
|
||||
// myModule has already been created! This is not what you want!
|
||||
.directive('myDirective', ['myCoolService', function (myCoolService) {
|
||||
// This directive definition throws unknown provider, because myCoolService
|
||||
// has been destroyed.
|
||||
}]);
|
||||
```
|
||||
|
||||
To fix this problem, make sure you only define each module with the
|
||||
`angular.module(name, [requires])` syntax once across your entire project.
|
||||
Retrieve it for subsequent use with `angular.module(name)`. The fixed example
|
||||
is shown below.
|
||||
|
||||
```
|
||||
angular.module('myModule', [])
|
||||
.service('myCoolService', function () { /* ... */ });
|
||||
|
||||
angular.module('myModule')
|
||||
.directive('myDirective', ['myCoolService', function (myCoolService) {
|
||||
// This directive definition does not throw unknown provider.
|
||||
}]);
|
||||
```
|
||||
|
||||
|
||||
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.
|
||||
```
|
||||
@@ -9,4 +9,4 @@ it hard to reason about whether some combination of concatenated values are
|
||||
unsafe to use and could easily lead to XSS.
|
||||
|
||||
For more information about how AngularJS helps keep your app secure, refer to
|
||||
the {@link ng.$sce $sce} API doc.
|
||||
the {@link ng.$sce $sce} API doc.
|
||||
@@ -14,34 +14,3 @@ perform this check - it's up to the developer to not expose such sensitive and p
|
||||
directly on the scope chain.
|
||||
|
||||
To resolve this error, avoid access to DOM nodes.
|
||||
|
||||
|
||||
# Event Handlers and Return Values
|
||||
|
||||
The `$parse:isecdom` error also occurs when an event handler invokes a function that returns a DOM
|
||||
node.
|
||||
|
||||
```html
|
||||
<button ng-click="iWillReturnDOM()">click me</button>
|
||||
```
|
||||
|
||||
```js
|
||||
$scope.iWillReturnDOM = function() {
|
||||
return someDomNode;
|
||||
}
|
||||
```
|
||||
|
||||
To fix this issue, avoid returning DOM nodes from event handlers.
|
||||
|
||||
*Note: This error often means that you are accessing DOM from your controllers, which is usually
|
||||
a sign of poor coding style that violates separation of concerns.*
|
||||
|
||||
|
||||
# Implicit Returns in CoffeeScript
|
||||
|
||||
This error can occur more frequently when using CoffeeScript, which has a feature called implicit
|
||||
returns. This language feature returns the last dereferenced object in the function when the
|
||||
function has no explicit return statement.
|
||||
|
||||
The solution in this scenario is to add an explicit return statement. For example `return false` to
|
||||
the function.
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $parse:isecff
|
||||
@fullName Referencing 'call', 'apply' and 'bind' Disallowed
|
||||
@description
|
||||
|
||||
Occurs when an expression attempts to invoke Function's 'call', 'apply' or 'bind'.
|
||||
|
||||
Angular bans the invocation of 'call', 'apply' and 'bind' from within expressions
|
||||
since access is a known way to modify the behaviour of existing functions.
|
||||
|
||||
To resolve this error, avoid using these methods in expressions.
|
||||
|
||||
Example expression that would result in this error:
|
||||
|
||||
```
|
||||
<div>{{user.sendInfo.call({}, true)}}</div>
|
||||
```
|
||||
@@ -1,27 +1,18 @@
|
||||
@ngdoc error
|
||||
@name $parse:isecfld
|
||||
@fullName Referencing Disallowed Field in Expression
|
||||
@fullName Referencing 'constructor' Field in Expression
|
||||
@description
|
||||
|
||||
Occurs when an expression attempts to access one of the following fields:
|
||||
Occurs when an expression attempts to access an objects constructor field.
|
||||
|
||||
* __proto__
|
||||
* __defineGetter__
|
||||
* __defineSetter__
|
||||
* __lookupGetter__
|
||||
* __lookupSetter__
|
||||
AngularJS bans constructor access from within expressions since constructor
|
||||
access is a known way to execute arbitrary Javascript code.
|
||||
|
||||
AngularJS bans access to these fields from within expressions since
|
||||
access is a known way to mess with native objects or
|
||||
to execute arbitrary Javascript code.
|
||||
To resolve this error, avoid constructor access. As a last resort, alias
|
||||
the constructor and access it through the alias instead.
|
||||
|
||||
To resolve this error, avoid using these fields in expressions. As a last resort,
|
||||
alias their value and access them through the alias instead.
|
||||
|
||||
Example expressions that would result in this error:
|
||||
Example expression that would result in this error:
|
||||
|
||||
```
|
||||
<div>{{user.__proto__.hasOwnProperty = $emit}}</div>
|
||||
|
||||
<div>{{user.__defineGetter__('name', noop)}}</div>
|
||||
<div>{{user.constructor.name}}</div>
|
||||
```
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
@ngdoc error
|
||||
@name $parse:isecobj
|
||||
@fullName Referencing Object Disallowed
|
||||
@description
|
||||
|
||||
Occurs when an expression attempts to access the 'Object' object (Root object in JavaScript).
|
||||
|
||||
Angular bans access to Object from within expressions since access is a known way to modify
|
||||
the behaviour of existing objects.
|
||||
|
||||
To resolve this error, avoid Object access.
|
||||
@@ -5,4 +5,4 @@
|
||||
|
||||
Occurs when you try to use the name `hasOwnProperty` as a name of a parameter.
|
||||
Generally, a name cannot be `hasOwnProperty` because it is used, internally, on a object
|
||||
and allowing such a name would break lookups on this object.
|
||||
and allowing such a name would break lookups on this object.
|
||||
@@ -3,310 +3,72 @@
|
||||
@fullName Action Already In Progress
|
||||
@description
|
||||
|
||||
At any point in time there can be only one `$digest` or `$apply` operation in progress. This is to
|
||||
prevent very hard to detect bugs from entering your application. The stack trace of this error
|
||||
allows you to trace the origin of the currently executing `$apply` or `$digest` call, which caused
|
||||
the error.
|
||||
At any point in time there can be only one `$digest` or $apply operation in progress.
|
||||
The stack trace of this error allows you to trace the origin of the currently executing $apply or $digest call.
|
||||
|
||||
## Background
|
||||
`$digest` or `$apply` are processing operational states of the Scope - data-structure in Angular that provides context for models and enables model mutation observation.
|
||||
|
||||
Angular uses a dirty-checking digest mechanism to monitor and update values of the scope during
|
||||
the processing of your application. The digest works by checking all the values that are being
|
||||
watched against their previous value and running any watch handlers that have been defined for those
|
||||
values that have changed.
|
||||
|
||||
This digest mechanism is triggered by calling `$digest` on a scope object. Normally you do not need
|
||||
to trigger a digest manually, because every external action that can trigger changes in your
|
||||
application, such as mouse events, timeouts or server responses, wrap the Angular application code
|
||||
in a block of code that will run `$digest` when the code completes.
|
||||
|
||||
You wrap Angular code in a block that will be followed by a `$digest` by calling `$apply` on a scope
|
||||
object. So, in pseudo-code, the process looks like this:
|
||||
|
||||
```
|
||||
element.on('mouseup', function() {
|
||||
scope.$apply(function() {
|
||||
$scope.doStuff();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
where `$apply()` looks something like:
|
||||
|
||||
```
|
||||
$apply = function(fn) {
|
||||
try {
|
||||
fn();
|
||||
} finally() {
|
||||
$digest();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Digest Phases
|
||||
|
||||
Angular keeps track of what phase of processing we are in, the relevant ones being `$apply` and
|
||||
`$digest`. Trying to reenter a `$digest` or `$apply` while one of them is already in progress is
|
||||
typically a sign of programming error that needs to be fixed. So Angular will throw this error when
|
||||
that occurs.
|
||||
|
||||
In most situations it should be well defined whether a piece of code will be run inside an `$apply`,
|
||||
in which case you should not be calling `$apply` or `$digest`, or it will be run outside, in which
|
||||
case you should wrap any code that will be interacting with Angular scope or services, in a call to
|
||||
`$apply`.
|
||||
|
||||
As an example, all Controller code should expect to be run within Angular, so it should have no need
|
||||
to call `$apply` or `$digest`. Conversely, code that is being trigger directly as a call back to
|
||||
some external event, from the DOM or 3rd party library, should expect that it is never called from
|
||||
within Angular, and so any Angular application code that it calls should first be wrapped in a call
|
||||
to $apply.
|
||||
|
||||
## Common Causes
|
||||
|
||||
Apart from simply incorrect calls to `$apply` or `$digest` there are some cases when you may get
|
||||
this error through no fault of your own.
|
||||
|
||||
### Inconsistent API (Sync/Async)
|
||||
Trying to reenter a `$digest` or `$apply` while one of them is already in progress is typically a sign of programming error that needs to be fixed.
|
||||
|
||||
This error is often seen when interacting with an API that is sometimes sync and sometimes async.
|
||||
|
||||
For example, imagine a 3rd party library that has a method which will retrieve data for us. Since it
|
||||
may be making an asynchronous call to a server, it accepts a callback function, which will be called
|
||||
when the data arrives.
|
||||
For example:
|
||||
|
||||
```
|
||||
function MyController($scope, thirdPartyComponent) {
|
||||
function MyController() {
|
||||
thirdPartyComponent.getData(function(someData) {
|
||||
$scope.$apply(function() {
|
||||
$scope.someData = someData;
|
||||
scope.$apply(function() {
|
||||
scope.someData = someData;
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
We expect that our callback will be called asynchronously, and so from outside Angular. Therefore, we
|
||||
correctly wrap our application code that interacts with Angular in a call to `$apply`.
|
||||
The controller constructor is always instantiated from within an $apply cycle, so if the third-party component called our callback synchronously, we'd be trying to enter the $apply again.
|
||||
|
||||
The problem comes if `getData()` decides to call the callback handler synchronously; perhaps it has
|
||||
the data already cached in memory and so it immediately calls the callback to return the data,
|
||||
synchronously.
|
||||
To resolve this type of issue, either fix the api to be always synchronous or asynchronous or wrap the call to the api with setTimeout call to make it always asynchronous.
|
||||
|
||||
Since, the `MyController` constructor is always instantiated from within an `$apply` call, our
|
||||
handler is trying to enter a new `$apply` block from within one.
|
||||
|
||||
This is not an ideal design choice on the part of the 3rd party library.
|
||||
Other situation that leads to this error is when you are trying to reuse a function to by using it as a callback for code that is called by various apis inside and outside of $apply.
|
||||
|
||||
To resolve this type of issue, either fix the api to be always synchronous or asynchronous or force
|
||||
your callback handler to always run asynchronously by using the `$timeout` service.
|
||||
For example:
|
||||
|
||||
```
|
||||
function MyController($scope, thirdPartyComponent) {
|
||||
thirdPartyComponent.getData(function(someData) {
|
||||
$timeout(function() {
|
||||
$scope.someData = someData;
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Here we have used `$timeout` to schedule the changes to the scope in a future call stack.
|
||||
By providing a timeout period of 0ms, this will occur as soon as possible and `$timeout` will ensure
|
||||
that the code will be called in a single `$apply` block.
|
||||
|
||||
### Triggering Events Programmatically
|
||||
|
||||
The other situation that often leads to this error is when you trigger code (such as a DOM event)
|
||||
programmatically (from within Angular), which is normally called by an external trigger.
|
||||
|
||||
For example, consider a directive that will set focus on an input control when a value in the scope
|
||||
is true:
|
||||
|
||||
```
|
||||
myApp.directive('setFocusIf', function() {
|
||||
myApp.directive('myDirective', function() {
|
||||
return {
|
||||
link: function($scope, $element, $attr) {
|
||||
$scope.$watch($attr.setFocusIf, function(value) {
|
||||
if ( value ) { $element[0].focus(); }
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
```
|
||||
link: function($scope, $element) {
|
||||
function doSomeWork() {
|
||||
$scope.$apply(function() {
|
||||
// do work here, and update the model
|
||||
};
|
||||
}
|
||||
|
||||
If we applied this directive to an input which also used the `ngFocus` directive to trigger some
|
||||
work when the element receives focus we will have a problem:
|
||||
|
||||
```
|
||||
<input set-focus-if="hasFocus" ng-focus="msg='has focus'">
|
||||
<button ng-click="hasFocus = true">Focus</button>
|
||||
```
|
||||
|
||||
In this setup, there are two ways to trigger ngFocus. First from a user interaction:
|
||||
|
||||
* Click on the input control
|
||||
* The input control gets focus
|
||||
* The `ngFocus` directive is triggered, setting `$scope.msg='has focus'` from within a new call to
|
||||
`$apply()`
|
||||
|
||||
Second programmatically:
|
||||
|
||||
* Click the button
|
||||
* The `ngClick` directive sets the value of `$scope.hasFocus` to true inside a call to `$apply`
|
||||
* The `$digest` runs, which triggers the watch inside the `setFocusIf` directive
|
||||
* The watch's handle runs, which gives the focus to the input
|
||||
* The `ngFocus` directive is triggered, setting `$scope.msg='has focus'` from within a new call to
|
||||
`$apply()`
|
||||
|
||||
In this second scenario, we are already inside a `$digest` when the ngFocus directive makes another
|
||||
call to `$apply()`, causing this error to be thrown.
|
||||
|
||||
It is possible to workaround this problem by moving the call to set the focus outside of the digest,
|
||||
by using `$timeout(fn, 0, false)`, where the `false` value tells Angular not to wrap this `fn` in a
|
||||
`$apply` block:
|
||||
|
||||
```
|
||||
myApp.directive('setFocusIf', function($timeout) {
|
||||
return {
|
||||
link: function($scope, $element, $attr) {
|
||||
$scope.$watch($attr.setFocusIf, function(value) {
|
||||
if ( value ) {
|
||||
$timeout(function() {
|
||||
// We must reevaluate the value in case it was changed by a subsequent
|
||||
// watch handler in the digest.
|
||||
if ( $scope.$eval($attr.setFocusIf) ) {
|
||||
$element[0].focus();
|
||||
}
|
||||
}, 0, false);
|
||||
}
|
||||
});
|
||||
$element.on('click', doSomeWork);
|
||||
doSomeWork(); // << this will throw an exception because templates are compiled within $apply
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Diagnosing This Error
|
||||
|
||||
When you get this error it can be rather daunting to diagnose the cause of the issue. The best
|
||||
course of action is to investigate the stack trace from the error. You need to look for places
|
||||
where `$apply` or `$digest` have been called and find the context in which this occurred.
|
||||
|
||||
There should be two calls:
|
||||
|
||||
* The first call is the good `$apply`/`$digest` and would normally be triggered by some event near
|
||||
the top of the call stack.
|
||||
|
||||
* The second call is the bad `$apply`/`$digest` and this is the one to investigate.
|
||||
|
||||
Once you have identified this call you work your way up the stack to see what the problem is.
|
||||
|
||||
* If the second call was made in your application code then you should look at why this code has been
|
||||
called from within a `$apply`/`$digest`. It may be a simple oversight or maybe it fits with the
|
||||
sync/async scenario described earlier.
|
||||
|
||||
* If the second call was made inside an Angular directive then it is likely that it matches the second
|
||||
programmatic event trigger scenario described earlier. In this case you may need to look further up
|
||||
the tree to what triggered the event in the first place.
|
||||
|
||||
### Example Problem
|
||||
|
||||
Let's look at how to investigate this error using the `setFocusIf` example from above. This example
|
||||
defines a new `setFocusIf` directive that sets the focus on the element where it is defined when the
|
||||
value of its attribute becomes true.
|
||||
|
||||
<example name="error-$rootScope-inprog" module="app">
|
||||
<file name="index.html">
|
||||
<button ng-click="focusInput = true">Focus</button>
|
||||
<input ng-focus="count = count + 1" set-focus-if="focusInput" />
|
||||
</file>
|
||||
<file name="app.js">
|
||||
angular.module('app', []).directive('setFocusIf', function() {
|
||||
return function link($scope, $element, $attr) {
|
||||
$scope.$watch($attr.setFocusIf, function(value) {
|
||||
if ( value ) { $element[0].focus(); }
|
||||
});
|
||||
};
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
||||
When you click on the button to cause the focus to occur we get our `$rootScope:inprog` error. The
|
||||
stacktrace looks like this:
|
||||
|
||||
```
|
||||
Error: [$rootScope:inprog]
|
||||
at Error (native)
|
||||
at angular.min.js:6:467
|
||||
at n (angular.min.js:105:60)
|
||||
at g.$get.g.$apply (angular.min.js:113:195)
|
||||
at HTMLInputElement.<anonymous> (angular.min.js:198:401)
|
||||
at angular.min.js:32:32
|
||||
at Array.forEach (native)
|
||||
at q (angular.min.js:7:295)
|
||||
at HTMLInputElement.c (angular.min.js:32:14)
|
||||
at Object.fn (app.js:12:38) angular.js:10111
|
||||
(anonymous function) angular.js:10111
|
||||
$get angular.js:7412
|
||||
$get.g.$apply angular.js:12738 <--- $apply
|
||||
(anonymous function) angular.js:19833 <--- called here
|
||||
(anonymous function) angular.js:2890
|
||||
q angular.js:320
|
||||
c angular.js:2889
|
||||
(anonymous function) app.js:12
|
||||
$get.g.$digest angular.js:12469
|
||||
$get.g.$apply angular.js:12742 <--- $apply
|
||||
(anonymous function) angular.js:19833 <--- called here
|
||||
(anonymous function) angular.js:2890
|
||||
q angular.js:320
|
||||
|
||||
The fix for the example above looks like this:
|
||||
```
|
||||
myApp.directive('myDirective', function() {
|
||||
return {
|
||||
link: function($scope, $element) {
|
||||
function doSomeWork() {
|
||||
// do work here, and update the model
|
||||
}
|
||||
|
||||
We can see (even though the Angular code is minified) that there were two calls to `$apply`, first
|
||||
on line `19833`, then on line `12738` of `angular.js`.
|
||||
$element.on('click', function() {
|
||||
$scope.$apply(doSomeWork); // <<< the $apply call was moved to the callsite that doesn't execute in $apply call already
|
||||
});
|
||||
|
||||
It is this second call that caused the error. If we look at the angular.js code, we can see that
|
||||
this call is made by an Angular directive.
|
||||
|
||||
```
|
||||
var ngEventDirectives = {};
|
||||
forEach(
|
||||
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
|
||||
function(name) {
|
||||
var directiveName = directiveNormalize('ng-' + name);
|
||||
ngEventDirectives[directiveName] = ['$parse', function($parse) {
|
||||
return {
|
||||
compile: function($element, attr) {
|
||||
var fn = $parse(attr[directiveName]);
|
||||
return function(scope, element, attr) {
|
||||
element.on(lowercase(name), function(event) {
|
||||
scope.$apply(function() {
|
||||
fn(scope, {$event:event});
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
};
|
||||
}];
|
||||
doSomeWork();
|
||||
}
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
It is not possible to tell which from the stack trace, but we happen to know in this case that it is
|
||||
the `ngFocus` directive.
|
||||
|
||||
Now look up the stack to see that our application code is only entered once in `app.js` at line `12`.
|
||||
This is where our problem is:
|
||||
});
|
||||
|
||||
```
|
||||
10: link: function($scope, $element, $attr) {
|
||||
11: $scope.$watch($attr.setFocusIf, function(value) {
|
||||
12: if ( value ) { $element[0].focus(); } <---- This is the source of the problem
|
||||
13: });
|
||||
14: }
|
||||
```
|
||||
|
||||
We can now see that the second `$apply` was caused by us programmatically triggering a DOM event
|
||||
(i.e. focus) to occur. We must fix this by moving the code outside of the $apply block using
|
||||
`$timeout` as described above.
|
||||
|
||||
## Further Reading
|
||||
To learn more about Angular processing model please check out the
|
||||
{@link guide/concepts concepts doc} as well as the {@link ng.$rootScope.Scope api} doc.
|
||||
To learn more about Angular processing model please check out the {@link guide/concepts concepts doc} as well as the {@link ng.$rootScope.Scope api} doc.
|
||||
|
||||
@@ -15,9 +15,9 @@ By default, only URLs that belong to the same origin are trusted. These are urls
|
||||
The {@link ng.directive:ngInclude ngInclude} directive and {@link guide/directive directives} that specify a `templateUrl` require a trusted resource URL.
|
||||
|
||||
To load templates from other domains and/or protocols, either adjust the {@link
|
||||
ng.$sceDelegateProvider#resourceUrlWhitelist whitelist}/ {@link
|
||||
ng.$sceDelegateProvider#resourceUrlBlacklist blacklist} or wrap the URL with a call to {@link
|
||||
ng.$sce#trustAsResourceUrl $sce.trustAsResourceUrl}.
|
||||
api/ng.$sceDelegateProvider#resourceUrlWhitelist whitelist}/ {@link
|
||||
api/ng.$sceDelegateProvider#resourceUrlBlacklist blacklist} or wrap the URL with a call to {@link
|
||||
api/ng.$sce#trustAsResourceUrl $sce.trustAsResourceUrl}.
|
||||
|
||||
**Note**: The browser's [Same Origin
|
||||
Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest) and
|
||||
|
||||
@@ -10,7 +10,6 @@ Angular's {@link ng.$sce Strict Contextual Escaping (SCE)} mode
|
||||
contexts to result in a value that is trusted as safe for use in such a context. (e.g. loading an
|
||||
Angular template from a URL requires that the URL is one considered safe for loading resources.)
|
||||
|
||||
This helps prevent XSS and other security issues. Read more at
|
||||
{@link ng.$sce Strict Contextual Escaping (SCE)}
|
||||
This helps prevent XSS and other security issues. Read more at {@link
|
||||
api/ng.$sce Strict Contextual Escaping (SCE)}
|
||||
|
||||
You may want to include the ngSanitize module to use the automatic sanitizing.
|
||||
|
||||
@@ -5,4 +5,4 @@
|
||||
|
||||
AngularJS often asserts that certain values will be present and truthy using a
|
||||
helper function. If the assertion fails, this error is thrown. To fix this problem,
|
||||
make sure that the value the assertion expects is defined and truthy.
|
||||
make sure that the value the assertion expects is defined and truthy.
|
||||
@@ -5,4 +5,4 @@
|
||||
|
||||
Occurs when you try to use the name `hasOwnProperty` in a context where it is not allow.
|
||||
Generally, a name cannot be `hasOwnProperty` because it is used, internally, on a object
|
||||
and allowing such a name would break lookups on this object.
|
||||
and allowing such a name would break lookups on this object.
|
||||
@@ -49,4 +49,4 @@ You can also get this error if you accidentally load AngularJS itself more than
|
||||
<script src="angular.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
```
|
||||
@@ -7,4 +7,4 @@ This error occurs when attempting to copy an object to itself. Calling {@link
|
||||
api/angular.copy angular.copy} with a `destination` object deletes
|
||||
all of the elements or properties on `destination` before copying to it. Copying
|
||||
an object to itself is not supported. Make sure to check your calls to
|
||||
`angular.copy` and avoid copying objects or arrays to themselves.
|
||||
`angular.copy` and avoid copying objects or arrays to themselves.
|
||||
@@ -7,4 +7,4 @@ Copying Window or Scope instances is not supported because of cyclical and self
|
||||
references. Avoid copying windows and scopes, as well as any other cyclical or
|
||||
self-referential structures. Note that trying to deep copy an object containing
|
||||
cyclical references that is neither a window nor a scope will cause infinite
|
||||
recursion and a stack overflow.
|
||||
recursion and a stack overflow.
|
||||
@@ -15,7 +15,7 @@ For example the issue can be triggered by this *invalid* code:
|
||||
|
||||
To resolve this error either ensure that the items in the collection have unique identity or use the `track by` syntax to specify how to track the association between models and DOM.
|
||||
|
||||
The example above can be resolved by using `track by $index`, which will cause the items to be keyed by their position in the array instead of their value:
|
||||
To resolve the example above can be resolved by using `track by $index`, which will cause the items to be keyed by their position in the array instead of their value:
|
||||
|
||||
```
|
||||
<div ng-repeat="value in [4, 4] track by $index"></div>
|
||||
|
||||
+153
-333
@@ -1,6 +1,5 @@
|
||||
@ngdoc overview
|
||||
@name Using $location
|
||||
@sortOrder 500
|
||||
@description
|
||||
|
||||
# What does it do?
|
||||
@@ -50,7 +49,7 @@ changes to $location are reflected into the browser address bar.
|
||||
<tr>
|
||||
<td class="head">integration with angular application life-cycle</td>
|
||||
<td>none</td>
|
||||
<td>knows about all internal life-cycle phases, integrates with {@link ng.$rootScope.Scope#$watch $watch}, ...</td>
|
||||
<td>knows about all internal life-cycle phases, integrates with $watch, ...</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
@@ -61,7 +60,7 @@ changes to $location are reflected into the browser address bar.
|
||||
|
||||
<tr>
|
||||
<td class="head">aware of docroot/context from which the application is loaded</td>
|
||||
<td>no - window.location.pathname returns "/docroot/actual/path"</td>
|
||||
<td>no - window.location.path returns "/docroot/actual/path"</td>
|
||||
<td>yes - $location.path() returns "/actual/path"</td>
|
||||
</tr>
|
||||
|
||||
@@ -164,7 +163,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/introduction.html#history-0). Applications use the same API in
|
||||
HTML5 [History API](http://www.w3.org/TR/html5/history.html). 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.
|
||||
|
||||
@@ -211,10 +210,6 @@ facilitate the browser URL change and history management.
|
||||
## Hashbang mode (default mode)
|
||||
|
||||
In this mode, `$location` uses Hashbang URLs in all browsers.
|
||||
Angular also does not intercept and rewrite links in this mode. I.e. links work
|
||||
as expected and also perform full page reloads when other parts of the url
|
||||
than the hash fragment was changed.
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
@@ -242,10 +237,24 @@ it('should show example', inject(
|
||||
));
|
||||
```
|
||||
|
||||
### Crawling your app
|
||||
|
||||
To allow indexing of your AJAX application, you have to add special meta tag in the head section of
|
||||
your document:
|
||||
|
||||
```html
|
||||
<meta name="fragment" content="!" />
|
||||
```
|
||||
|
||||
This will cause crawler bot to request links with `_escaped_fragment_` param so that your server
|
||||
can recognize the crawler and serve a HTML snapshots. For more information about this technique,
|
||||
see [Making AJAX Applications
|
||||
Crawlable](http://code.google.com/web/ajaxcrawling/docs/specification.html).
|
||||
|
||||
## HTML5 mode
|
||||
|
||||
In HTML5 mode, the `$location` service getters and setters interact with the browser URL address
|
||||
through the HTML5 history API. This allows for use of regular URL path and search segments,
|
||||
through the HTML5 history API, which 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
|
||||
@@ -254,10 +263,6 @@ having to worry about whether the browser displaying your app supports the histo
|
||||
- Opening a regular URL in a legacy browser -> redirects to a hashbang URL
|
||||
- Opening hashbang URL in a modern browser -> rewrites to a regular URL
|
||||
|
||||
Note that in this mode, Angular intercepts all links (subject to the "Html link rewriting" rules below)
|
||||
and updates the url in a way that never performs a full page reload.
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
```js
|
||||
@@ -306,8 +311,8 @@ history API or not; the `$location` service makes this transparent to you.
|
||||
|
||||
### Html link rewriting
|
||||
|
||||
When you use HTML5 history API mode, you will not need special hashbang links. All you have to do
|
||||
is specify regular URL links, such as: `<a href="/some?foo=bar">link</a>`
|
||||
When you use HTML5 history API mode, you will need different links in different browsers, but all you
|
||||
have to do is specify regular URL links, such as: `<a href="/some?foo=bar">link</a>`
|
||||
|
||||
When a user clicks on this link,
|
||||
|
||||
@@ -322,15 +327,37 @@ reload to the original link.
|
||||
Example: `<a href="/ext/link?a=b" target="_self">link</a>`
|
||||
- Absolute links that go to a different domain<br>
|
||||
Example: `<a href="http://angularjs.org/">link</a>`
|
||||
- Links starting with '/' that lead to a different base path<br>
|
||||
- Links starting with '/' that lead to a different base path when base is defined<br>
|
||||
Example: `<a href="/not-my-base/link">link</a>`
|
||||
|
||||
When running Angular in the root of a domain, along side perhaps a normal application in the same
|
||||
directory, the "otherwise" route handler will try to handle all the URLs, including ones that map
|
||||
to static files.
|
||||
|
||||
To prevent this, you can set your base href for the app to `<base href=".">` and then prefix links
|
||||
to URLs that should be handled with `.`. Now, links to locations, which are not to be routed by Angular,
|
||||
are not prefixed with `.` and will not be intercepted by the `otherwise` rule in your `$routeProvider`.
|
||||
|
||||
|
||||
### Server side
|
||||
|
||||
Using this mode requires URL rewriting on server side, basically you have to rewrite all your links
|
||||
to entry point of your application (e.g. index.html)
|
||||
|
||||
### Crawling your app
|
||||
|
||||
If you want your AJAX application to be indexed by web crawlers, you will need to add the following
|
||||
meta tag to the HEAD section of your document:
|
||||
|
||||
```html
|
||||
<meta name="fragment" content="!" />
|
||||
```
|
||||
|
||||
This statement causes a crawler to request links with an empty `_escaped_fragment_` parameter so that
|
||||
your server can recognize the crawler and serve it HTML snapshots. For more information about this
|
||||
technique, see [Making AJAX
|
||||
Applications Crawlable](http://code.google.com/web/ajaxcrawling/docs/specification.html).
|
||||
|
||||
### Relative links
|
||||
|
||||
Be sure to check all relative links, images, scripts etc. You must either specify the url base in
|
||||
@@ -359,309 +386,119 @@ Note that when you type hashbang url into first browser (or vice versa) it doesn
|
||||
redirect to regular / hashbang url, as this conversion happens only during parsing the initial URL
|
||||
= on page reload.
|
||||
|
||||
In these examples we use `<base href="/base/index.html" />`
|
||||
|
||||
#### Browser in HTML5 mode
|
||||
<example module="html5-mode" name="location-html5-mode">
|
||||
In this examples we use `<base href="/base/index.html" />`
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-controller="LocationController">
|
||||
<div ng-address-bar></div><br><br>
|
||||
<div>
|
||||
$location.protocol() = <span ng-bind="$location.protocol()"></span> <br>
|
||||
$location.host() = <span ng-bind="$location.host()"></span> <br>
|
||||
$location.port() = <span ng-bind="$location.port()"></span> <br>
|
||||
$location.path() = <span ng-bind="$location.path()"></span> <br>
|
||||
$location.search() = <span ng-bind="$location.search()"></span> <br>
|
||||
$location.hash() = <span ng-bind="$location.hash()"></span> <br>
|
||||
</div>
|
||||
<div id="navigation">
|
||||
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
<div id="html5-mode" ng-controller="Html5Cntl">
|
||||
<h3>Browser with History API</h3>
|
||||
<div ng-address-bar browser="html5"></div><br><br>
|
||||
$location.protocol() = {{$location.protocol()}}<br>
|
||||
$location.host() = {{$location.host()}}<br>
|
||||
$location.port() = {{$location.port()}}<br>
|
||||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
|
||||
<div id="hashbang-mode" ng-controller="HashbangCntl">
|
||||
<h3>Browser without History API</h3>
|
||||
<div ng-address-bar browser="hashbang"></div><br><br>
|
||||
$location.protocol() = {{$location.protocol()}}<br>
|
||||
$location.host() = {{$location.host()}}<br>
|
||||
$location.port() = {{$location.port()}}<br>
|
||||
$location.path() = {{$location.path()}}<br>
|
||||
$location.search() = {{$location.search()}}<br>
|
||||
$location.hash() = {{$location.hash()}}<br>
|
||||
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
</file>
|
||||
<file name="app.js">
|
||||
angular.module('html5-mode', ['fake-browser', 'address-bar'])
|
||||
|
||||
.constant('initUrl', 'http://www.example.com/base/path?a=b#h')
|
||||
.constant('baseHref', '/base/index.html')
|
||||
.value('$sniffer', { history: true })
|
||||
|
||||
.controller("LocationController", function($scope, $location) {
|
||||
$scope.$location = {};
|
||||
angular.forEach("protocol host port path search hash".split(" "), function(method){
|
||||
$scope.$location[method] = function(){
|
||||
var result = $location[method].call($location);
|
||||
return angular.isObject(result) ? angular.toJson(result) : result;
|
||||
};
|
||||
});
|
||||
})
|
||||
|
||||
.config(function($locationProvider) {
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
})
|
||||
|
||||
.run(function($rootElement) {
|
||||
$rootElement.on('click', function(e) { e.stopPropagation(); });
|
||||
});
|
||||
</file>
|
||||
|
||||
<file name="fakeBrowser.js">
|
||||
angular.module('fake-browser', [])
|
||||
|
||||
.config(function($provide) {
|
||||
$provide.decorator('$browser', function($delegate, baseHref, initUrl) {
|
||||
|
||||
$delegate.onUrlChange = function(fn) {
|
||||
this.urlChange = fn;
|
||||
};
|
||||
|
||||
$delegate.url = function() {
|
||||
return initUrl;
|
||||
<file name="script.js">
|
||||
function FakeBrowser(initUrl, baseHref) {
|
||||
this.onUrlChange = function(fn) {
|
||||
this.urlChange = fn;
|
||||
};
|
||||
|
||||
$delegate.defer = function(fn, delay) {
|
||||
setTimeout(function() { fn(); }, delay || 0);
|
||||
};
|
||||
this.url = function() {
|
||||
return initUrl;
|
||||
};
|
||||
|
||||
$delegate.baseHref = function() {
|
||||
return baseHref;
|
||||
};
|
||||
this.defer = function(fn, delay) {
|
||||
setTimeout(function() { fn(); }, delay || 0);
|
||||
};
|
||||
|
||||
return $delegate;
|
||||
});
|
||||
});
|
||||
</file>
|
||||
this.baseHref = function() {
|
||||
return baseHref;
|
||||
};
|
||||
|
||||
<file name="addressBar.js">
|
||||
angular.module('address-bar', [])
|
||||
.directive('ngAddressBar', function($browser, $timeout) {
|
||||
return {
|
||||
template: 'Address: <input id="addressBar" type="text" style="width: 400px" >',
|
||||
link: function(scope, element, attrs){
|
||||
var input = element.children("input"), delay;
|
||||
this.notifyWhenOutstandingRequests = angular.noop;
|
||||
}
|
||||
|
||||
input.on('keypress keyup keydown', function(event) {
|
||||
delay = (!delay ? $timeout(fireUrlChange, 250) : null);
|
||||
event.stopPropagation();
|
||||
})
|
||||
.val($browser.url());
|
||||
var browsers = {
|
||||
html5: new FakeBrowser('http://www.example.com/base/path?a=b#h', '/base/index.html'),
|
||||
hashbang: new FakeBrowser('http://www.example.com/base/index.html#!/path?a=b#h', '/base/index.html')
|
||||
};
|
||||
|
||||
$browser.url = function(url) {
|
||||
return url ? input.val(url) : input.val();
|
||||
function Html5Cntl($scope, $location) {
|
||||
$scope.$location = $location;
|
||||
}
|
||||
|
||||
function HashbangCntl($scope, $location) {
|
||||
$scope.$location = $location;
|
||||
}
|
||||
|
||||
function initEnv(name) {
|
||||
var root = angular.element(document.getElementById(name + '-mode'));
|
||||
// We must kill a link to the injector for this element otherwise angular will
|
||||
// complain that it has been bootstrapped already.
|
||||
root.data('$injector', null);
|
||||
angular.bootstrap(root, [function($compileProvider, $locationProvider, $provide){
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
|
||||
$provide.value('$browser', browsers[name]);
|
||||
$provide.value('$sniffer', {history: name == 'html5'});
|
||||
|
||||
$compileProvider.directive('ngAddressBar', function() {
|
||||
return function(scope, elm, attrs) {
|
||||
var browser = browsers[attrs.browser],
|
||||
input = angular.element('<input type="text" style="width: 400px">').val(browser.url()),
|
||||
delay;
|
||||
|
||||
input.on('keypress keyup keydown', function() {
|
||||
if (!delay) {
|
||||
delay = setTimeout(fireUrlChange, 250);
|
||||
}
|
||||
});
|
||||
|
||||
browser.url = function(url) {
|
||||
return input.val(url);
|
||||
};
|
||||
|
||||
elm.append('Address: ').append(input);
|
||||
|
||||
function fireUrlChange() {
|
||||
delay = null;
|
||||
browser.urlChange(input.val());
|
||||
}
|
||||
};
|
||||
|
||||
function fireUrlChange() {
|
||||
delay = null;
|
||||
$browser.urlChange(input.val());
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
</file>
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
|
||||
var addressBar = element(by.css("#addressBar")),
|
||||
url = 'http://www.example.com/base/path?a=b#h';
|
||||
|
||||
|
||||
it("should show fake browser info on load", function(){
|
||||
expect(addressBar.getAttribute('value')).toBe(url);
|
||||
|
||||
expect(element(by.binding('$location.protocol')).getText()).toBe('http');
|
||||
expect(element(by.binding('$location.host')).getText()).toBe('www.example.com');
|
||||
expect(element(by.binding('$location.port')).getText()).toBe('80');
|
||||
expect(element(by.binding('$location.path')).getText()).toBe('/path');
|
||||
expect(element(by.binding('$location.search')).getText()).toBe('{"a":"b"}');
|
||||
expect(element(by.binding('$location.hash')).getText()).toBe('h');
|
||||
|
||||
});
|
||||
|
||||
it("should change $location accordingly", function(){
|
||||
var navigation = element.all(by.css("#navigation a"));
|
||||
|
||||
navigation.get(0).click();
|
||||
|
||||
expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/first?a=b");
|
||||
|
||||
expect(element(by.binding('$location.protocol')).getText()).toBe('http');
|
||||
expect(element(by.binding('$location.host')).getText()).toBe('www.example.com');
|
||||
expect(element(by.binding('$location.port')).getText()).toBe('80');
|
||||
expect(element(by.binding('$location.path')).getText()).toBe('/first');
|
||||
expect(element(by.binding('$location.search')).getText()).toBe('{"a":"b"}');
|
||||
expect(element(by.binding('$location.hash')).getText()).toBe('');
|
||||
|
||||
|
||||
navigation.get(1).click();
|
||||
|
||||
expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/sec/ond?flag#hash");
|
||||
|
||||
expect(element(by.binding('$location.protocol')).getText()).toBe('http');
|
||||
expect(element(by.binding('$location.host')).getText()).toBe('www.example.com');
|
||||
expect(element(by.binding('$location.port')).getText()).toBe('80');
|
||||
expect(element(by.binding('$location.path')).getText()).toBe('/sec/ond');
|
||||
expect(element(by.binding('$location.search')).getText()).toBe('{"flag":true}');
|
||||
expect(element(by.binding('$location.hash')).getText()).toBe('hash');
|
||||
});
|
||||
|
||||
</file>
|
||||
|
||||
</example>
|
||||
|
||||
####Browser in HTML5 Fallback mode (Hashbang mode)
|
||||
<example module="hashbang-mode" name="location-hashbang-mode">
|
||||
<file name="index.html">
|
||||
<div ng-controller="LocationController">
|
||||
<div ng-address-bar></div><br><br>
|
||||
<div>
|
||||
$location.protocol() = <span ng-bind="$location.protocol()"></span> <br>
|
||||
$location.host() = <span ng-bind="$location.host()"></span> <br>
|
||||
$location.port() = <span ng-bind="$location.port()"></span> <br>
|
||||
$location.path() = <span ng-bind="$location.path()"></span> <br>
|
||||
$location.search() = <span ng-bind="$location.search()"></span> <br>
|
||||
$location.hash() = <span ng-bind="$location.hash()"></span> <br>
|
||||
</div>
|
||||
<div id="navigation">
|
||||
<a href="http://www.example.com/base/first?a=b">/base/first?a=b</a> |
|
||||
<a href="http://www.example.com/base/sec/ond?flag#hash">sec/ond?flag#hash</a> |
|
||||
<a href="/other-base/another?search">external</a>
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
<file name="app.js">
|
||||
angular.module('hashbang-mode', ['fake-browser', 'address-bar'])
|
||||
|
||||
.constant('initUrl', 'http://www.example.com/base/index.html#!/path?a=b#h')
|
||||
.constant('baseHref', '/base/index.html')
|
||||
.value('$sniffer', { history: false })
|
||||
|
||||
.config(function($locationProvider) {
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
})
|
||||
|
||||
.controller("LocationController", function($scope, $location) {
|
||||
$scope.$location = {};
|
||||
angular.forEach("protocol host port path search hash".split(" "), function(method){
|
||||
$scope.$location[method] = function(){
|
||||
var result = $location[method].call($location);
|
||||
return angular.isObject(result) ? angular.toJson(result) : result;
|
||||
};
|
||||
});
|
||||
})
|
||||
|
||||
.run(function($rootElement) {
|
||||
$rootElement.on('click', function(e) {
|
||||
});
|
||||
}]);
|
||||
root.on('click', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
initEnv('html5');
|
||||
initEnv('hashbang');
|
||||
</file>
|
||||
|
||||
<file name="fakeBrowser.js">
|
||||
angular.module('fake-browser', [])
|
||||
|
||||
.config(function($provide) {
|
||||
$provide.decorator('$browser', function($delegate, baseHref, initUrl) {
|
||||
|
||||
$delegate.onUrlChange = function(fn) {
|
||||
this.urlChange = fn;
|
||||
};
|
||||
|
||||
$delegate.url = function() {
|
||||
return initUrl;
|
||||
};
|
||||
|
||||
$delegate.defer = function(fn, delay) {
|
||||
setTimeout(function() { fn(); }, delay || 0);
|
||||
};
|
||||
|
||||
$delegate.baseHref = function() {
|
||||
return baseHref;
|
||||
};
|
||||
|
||||
return $delegate;
|
||||
});
|
||||
});
|
||||
</file>
|
||||
|
||||
|
||||
<file name="addressBar.js">
|
||||
angular.module('address-bar', [])
|
||||
.directive('ngAddressBar', function($browser, $timeout) {
|
||||
return {
|
||||
template: 'Address: <input id="addressBar" type="text" style="width: 400px" >',
|
||||
link: function(scope, element, attrs){
|
||||
var input = element.children("input"), delay;
|
||||
|
||||
input.on('keypress keyup keydown', function(event) {
|
||||
delay = (!delay ? $timeout(fireUrlChange, 250) : null);
|
||||
event.stopPropagation();
|
||||
})
|
||||
.val($browser.url());
|
||||
|
||||
$browser.url = function(url) {
|
||||
return url ? input.val(url) : input.val();
|
||||
};
|
||||
|
||||
function fireUrlChange() {
|
||||
delay = null;
|
||||
$browser.urlChange(input.val());
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
</file>
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
|
||||
var addressBar = element(by.css("#addressBar")),
|
||||
url = 'http://www.example.com/base/index.html#!/path?a=b#h';
|
||||
|
||||
it("should show fake browser info on load", function(){
|
||||
expect(addressBar.getAttribute('value')).toBe(url);
|
||||
|
||||
expect(element(by.binding('$location.protocol')).getText()).toBe('http');
|
||||
expect(element(by.binding('$location.host')).getText()).toBe('www.example.com');
|
||||
expect(element(by.binding('$location.port')).getText()).toBe('80');
|
||||
expect(element(by.binding('$location.path')).getText()).toBe('/path');
|
||||
expect(element(by.binding('$location.search')).getText()).toBe('{"a":"b"}');
|
||||
expect(element(by.binding('$location.hash')).getText()).toBe('h');
|
||||
|
||||
});
|
||||
|
||||
it("should change $location accordingly", function(){
|
||||
var navigation = element.all(by.css("#navigation a"));
|
||||
|
||||
navigation.get(0).click();
|
||||
|
||||
expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/index.html#!/first?a=b");
|
||||
|
||||
expect(element(by.binding('$location.protocol')).getText()).toBe('http');
|
||||
expect(element(by.binding('$location.host')).getText()).toBe('www.example.com');
|
||||
expect(element(by.binding('$location.port')).getText()).toBe('80');
|
||||
expect(element(by.binding('$location.path')).getText()).toBe('/first');
|
||||
expect(element(by.binding('$location.search')).getText()).toBe('{"a":"b"}');
|
||||
expect(element(by.binding('$location.hash')).getText()).toBe('');
|
||||
|
||||
|
||||
navigation.get(1).click();
|
||||
|
||||
expect(addressBar.getAttribute('value')).toBe("http://www.example.com/base/index.html#!/sec/ond?flag#hash");
|
||||
|
||||
expect(element(by.binding('$location.protocol')).getText()).toBe('http');
|
||||
expect(element(by.binding('$location.host')).getText()).toBe('www.example.com');
|
||||
expect(element(by.binding('$location.port')).getText()).toBe('80');
|
||||
expect(element(by.binding('$location.path')).getText()).toBe('/sec/ond');
|
||||
expect(element(by.binding('$location.search')).getText()).toBe('{"flag":true}');
|
||||
expect(element(by.binding('$location.hash')).getText()).toBe('hash');
|
||||
|
||||
});
|
||||
</file>
|
||||
|
||||
</example>
|
||||
|
||||
|
||||
# Caveats
|
||||
|
||||
## Page reload navigation
|
||||
@@ -673,12 +510,10 @@ use a lower level API, {@link ng.$window $window.location.href}.
|
||||
## Using $location outside of the scope life-cycle
|
||||
|
||||
`$location` knows about Angular's {@link ng.$rootScope.Scope scope} life-cycle. When a URL changes in
|
||||
the browser it updates the `$location` and calls `$apply` so that all
|
||||
{@link ng.$rootScope.Scope#$watch $watchers} /
|
||||
{@link ng.$compile.directive.Attributes#$observe $observers} are notified.
|
||||
the browser it updates the `$location` and calls `$apply` so that all $watchers / $observers are
|
||||
notified.
|
||||
When you change the `$location` inside the `$digest` phase everything is ok; `$location` will
|
||||
propagate this change into browser and will notify all the {@link ng.$rootScope.Scope#$watch $watchers} /
|
||||
{@link ng.$compile.directive.Attributes#$observe $observers}.
|
||||
propagate this change into browser and will notify all the $watchers / $observers.
|
||||
When you want to change the `$location` from outside Angular (for example, through a DOM Event or
|
||||
during testing) - you must call `$apply` to propagate the changes.
|
||||
|
||||
@@ -690,20 +525,6 @@ forward slash if it is missing.
|
||||
Note that the `!` prefix in the hashbang mode is not part of `$location.path()`; it is actually
|
||||
hashPrefix.
|
||||
|
||||
## Crawling your app
|
||||
|
||||
To allow indexing of your AJAX application, you have to add special meta tag in the head section of
|
||||
your document:
|
||||
|
||||
```html
|
||||
<meta name="fragment" content="!" />
|
||||
```
|
||||
|
||||
This will cause crawler bot to request links with `_escaped_fragment_` param so that your server
|
||||
can recognize the crawler and serve a HTML snapshots. For more information about this technique,
|
||||
see [Making AJAX Applications
|
||||
Crawlable](http://code.google.com/web/ajaxcrawling/docs/specification.html).
|
||||
|
||||
|
||||
# Testing with the $location service
|
||||
|
||||
@@ -811,26 +632,25 @@ then uses the information it obtains to compose hashbang URLs (such as
|
||||
|
||||
The Angular's compiler currently does not support two-way binding for methods (see [issue](https://github.com/angular/angular.js/issues/404)). If you should require two-way binding
|
||||
to the $location object (using {@link input[text] ngModel} directive on an input
|
||||
field), you will need to specify an extra model property (e.g. `locationPath`) with two {@link ng.$rootScope.Scope#$watch $watchers}
|
||||
field), you will need to specify an extra model property (e.g. `locationPath`) with two watchers
|
||||
which push $location updates in both directions. For example:
|
||||
<example module="locationExample">
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<div ng-controller="LocationController">
|
||||
<input type="text" ng-model="locationPath" />
|
||||
</div>
|
||||
</file>
|
||||
<file name="script.js">
|
||||
angular.module('locationExample', [])
|
||||
.controller('LocationController', ['$scope', '$location', function ($scope, $location) {
|
||||
$scope.$watch('locationPath', function(path) {
|
||||
$location.path(path);
|
||||
});
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function(path) {
|
||||
$scope.locationPath = path;
|
||||
});
|
||||
}]);
|
||||
function LocationController($scope, $location) {
|
||||
$scope.$watch('locationPath', function(path) {
|
||||
$location.path(path);
|
||||
});
|
||||
$scope.$watch(function() {
|
||||
return $location.path();
|
||||
}, function(path) {
|
||||
$scope.locationPath = path;
|
||||
});
|
||||
}
|
||||
</file>
|
||||
</example>
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
@ngdoc overview
|
||||
@name Animations
|
||||
@sortOrder 310
|
||||
@description
|
||||
|
||||
|
||||
@@ -252,18 +251,15 @@ Although the CSS is a little different then what we saw before, the idea is the
|
||||
A handful of common AngularJS directives support and trigger animation hooks whenever any major event occurs during its life cycle.
|
||||
The table below explains in detail which animation events are triggered
|
||||
|
||||
-| Directive | Supported Animations |
|
||||
-|-----------------------------------------------------------------|--------------------------------------------------------------------------|
|
||||
-| {@link ng.directive:ngRepeat#usage_animations ngRepeat} | enter, leave and move |
|
||||
-| {@link ngRoute.directive:ngView#usage_animations ngView} | enter and leave |
|
||||
-| {@link ng.directive:ngInclude#usage_animations ngInclude} | enter and leave |
|
||||
-| {@link ng.directive:ngSwitch#usage_animations ngSwitch} | enter and leave |
|
||||
-| {@link ng.directive:ngIf#usage_animations ngIf} | enter and leave |
|
||||
-| {@link ng.directive:ngClass#usage_animations ngClass} | add and remove |
|
||||
-| {@link ng.directive:ngShow#usage_animations ngShow & ngHide} | add and remove (the ng-hide class value) |
|
||||
-| {@link ng.directive:form#usage_animations form} | add and remove (dirty, pristine, valid, invalid & all other validations) |
|
||||
-| {@link ng.directive:ngModel#usage_animations ngModel} | add and remove (dirty, pristine, valid, invalid & all other validations) |
|
||||
|
||||
| Directive | Supported Animations |
|
||||
|-------------------------------------------------------------------------------------|------------------------------------------|
|
||||
| {@link ng.directive:ngRepeat#usage_animations ngRepeat} | enter, leave, and move |
|
||||
| {@link ngRoute.directive:ngView#usage_animations ngView} | enter and leave |
|
||||
| {@link ng.directive:ngInclude#usage_animations ngInclude} | enter and leave |
|
||||
| {@link ng.directive:ngSwitch#usage_animations ngSwitch} | enter and leave |
|
||||
| {@link ng.directive:ngIf#usage_animations ngIf} | enter and leave |
|
||||
| {@link ng.directive:ngClass#usage_animations ngClass or {{class}}} | add and remove |
|
||||
| {@link ng.directive:ngShow#usage_animations ngShow & ngHide} | add and remove (the ng-hide class value) |
|
||||
|
||||
For a full breakdown of the steps involved during each animation event, refer to the {@link ngAnimate.$animate API docs}.
|
||||
|
||||
@@ -275,7 +271,7 @@ making calls to it when needed.
|
||||
```js
|
||||
myModule.directive('my-directive', ['$animate', function($animate) {
|
||||
return function(element, scope, attrs) {
|
||||
element.bind('click', function() {
|
||||
element.on('click', function() {
|
||||
if(element.hasClass('clicked')) {
|
||||
$animate.removeClass(element, 'clicked');
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
@ngdoc overview
|
||||
@name Bootstrap
|
||||
@sortOrder 350
|
||||
@description
|
||||
|
||||
# Bootstrap
|
||||
@@ -20,7 +19,7 @@ initialization.
|
||||
<html xmlns:ng="http://angularjs.org" ng-app>
|
||||
<body>
|
||||
...
|
||||
<script src="angular.js"></script>
|
||||
<script src="angular.js">
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
@@ -91,10 +90,8 @@ Here is an example of manually initializing Angular:
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
<div ng-controller="MyController">
|
||||
Hello {{greetMe}}!
|
||||
</div>
|
||||
<script src="http://code.angularjs.org/snapshot/angular.js"></script>
|
||||
Hello {{'World'}}!
|
||||
<script src="http://code.angularjs.org/angular.js"></script>
|
||||
|
||||
<script>
|
||||
angular.module('myApp', [])
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
@ngdoc overview
|
||||
@name HTML Compiler
|
||||
@sortOrder 330
|
||||
@description
|
||||
|
||||
# HTML Compiler
|
||||
|
||||
<div class="alert alert-warning">
|
||||
**Note:** this guide is targeted towards developers who are already familiar with AngularJS basics.
|
||||
|
||||
@@ -14,7 +11,7 @@ If you want a deeper look into Angular's compilation process, you're in the righ
|
||||
</div>
|
||||
|
||||
|
||||
## Overview
|
||||
# Overview
|
||||
|
||||
Angular's {@link ng.$compile HTML compiler} allows the developer to teach the
|
||||
browser new HTML syntax. The compiler allows you to attach behavior to any HTML element or attribute
|
||||
@@ -87,9 +84,7 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
|
||||
position: 'relative',
|
||||
border: '1px solid red',
|
||||
backgroundColor: 'lightgrey',
|
||||
cursor: 'pointer',
|
||||
display: 'block',
|
||||
width: '65px'
|
||||
cursor: 'pointer'
|
||||
});
|
||||
element.on('mousedown', function(event) {
|
||||
// Prevent default dragging of selected content
|
||||
@@ -110,8 +105,8 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
|
||||
}
|
||||
|
||||
function mouseup() {
|
||||
$document.unbind('mousemove', mousemove);
|
||||
$document.unbind('mouseup', mouseup);
|
||||
$document.off('mousemove', mousemove);
|
||||
$document.off('mouseup', mouseup);
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -203,7 +198,7 @@ This should help give you an idea of what Angular does internally.
|
||||
|
||||
// Step 3: link the compiled template with the scope.
|
||||
var element = linkFn(scope);
|
||||
|
||||
|
||||
// Step 4: Append to DOM (optional)
|
||||
parent.appendChild(element);
|
||||
```
|
||||
@@ -231,7 +226,7 @@ moved to the compile function for performance reasons.
|
||||
To understand, let's look at a real-world example with `ngRepeat`:
|
||||
|
||||
```html
|
||||
Hello {{user.name}}, you have these actions:
|
||||
Hello {{user}}, you have these actions:
|
||||
<ul>
|
||||
<li ng-repeat="action in user.actions">
|
||||
{{action.description}}
|
||||
@@ -241,7 +236,7 @@ Hello {{user.name}}, you have these actions:
|
||||
|
||||
When the above example is compiled, the compiler visits every node and looks for directives.
|
||||
|
||||
`{{user.name}}` matches the {@link ng.$interpolate interpolation directive}
|
||||
`{{user}}` matches the {@link ng.$interpolate interpolation directive}
|
||||
and `ng-repeat` matches the {@link ng.directive:ngRepeat `ngRepeat` directive}.
|
||||
|
||||
But {@link ng.directive:ngRepeat ngRepeat} has a dilemma.
|
||||
@@ -332,7 +327,7 @@ The first issue we have to solve is that the dialog box template expects `title`
|
||||
But we would like the template's scope property `title` to be the result of interpolating the
|
||||
`<dialog>` element's `title` attribute (i.e. `"Hello {{username}}"`). Furthermore, the buttons expect
|
||||
the `onOk` and `onCancel` functions to be present in the scope. This limits the usefulness of the
|
||||
widget. To solve the mapping issue we use the `scope` to create local variables which the template
|
||||
widget. To solve the mapping issue we use the `locals` to create local variables which the template
|
||||
expects as follows:
|
||||
|
||||
```js
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user