Compare commits

..

No commits in common. "master" and "v1.1.4" have entirely different histories.

713 changed files with 50594 additions and 62680 deletions

View file

@ -1,4 +0,0 @@
{
"directory": "bower_components",
"json": "bower.json"
}

14
.gitignore vendored
View file

@ -1,4 +1,6 @@
/build/
build/
angularjs.netrc
jstd.log
.DS_Store
gen_docs.disable
test.disable
@ -6,15 +8,7 @@ regression/temp*.html
performance/temp*.html
.idea/workspace.xml
*~
*.swp
angular.js.tmproj
/node_modules/
/components/
/bower_components/
node_modules
angular.xcodeproj
.idea
.agignore
libpeerconnection.log
npm-debug.log
/tmp/
/scripts/bower/bower-*

View file

@ -1,3 +0,0 @@
{
"disallowKeywords": ["with"]
}

View file

@ -1,22 +0,0 @@
// This is an incomplete TODO list of checks we want to start enforcing
//
// The goal is to enable these checks one by one by moving them to .jscs.json along with commits
// that correct the existing code base issues and make the new check pass.
{
"requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"],
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
"disallowLeftStickedOperators": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
"disallowRightStickedOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
"requireRightStickedOperators": ["!"],
"requireLeftStickedOperators": [","],
"disallowImplicitTypeConversion": ["string"],
"disallowMultipleLineBreaks": true,
"disallowKeywordsOnNewLine": ["else"],
"disallowTrailingWhitespace": true,
"requireLineFeedAtFileEnd": true,
"validateJSDoc": {
"checkParamNames": true,
"requireParamTypes": true
}
}

View file

@ -1,28 +1,13 @@
language: node_js
node_js:
- 0.10
env:
matrix:
- JOB=unit
- JOB=e2e
global:
- SAUCE_USERNAME=angular-ci
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
- LOGS_DIR=/tmp/angular-build/logs
- BROWSER_PROVIDER_READY_FILE=/tmp/sauce-connect-ready
- 0.8
before_script:
- mkdir -p $LOGS_DIR
- ./lib/sauce/sauce_connect_setup.sh
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- npm install -g grunt-cli
- grunt bower
- grunt bower
- grunt package-without-bower
- ./scripts/travis/wait_for_browser_provider.sh
- grunt package
- grunt webserver > /dev/null &
script:
- ./scripts/travis/build.sh
after_script:
- ./scripts/travis/print_logs.sh
- grunt test --browsers Firefox --reporters=dots

File diff suppressed because it is too large Load diff

View file

@ -1,262 +1,32 @@
#Contributing to AngularJS
## Submitting issues
We'd love for you to contribute to our source code and to make AngularJS even better than it is
today! Here are the guidelines we'd like you to follow:
If you have questions about how to use AngularJS, please direct these to the
[Google Group][groups] discussion list or [StackOverflow][stackoverflow]. We are
also available on [IRC][irc].
## Got a Question or Problem?
### Guidelines
If you have questions about how to use AngularJS, please direct these to the [Google Group][groups]
discussion list or [StackOverflow][stackoverflow]. We are also available on [IRC][irc].
## Found an Issue?
If you find a bug in the source code or a mistake in the documentation, you can help us by
submitting and issue to our [GitHub Repository][github]. Even better you can submit a Pull Request
with a fix.
***Localization Issue:*** *Angular.js uses the [Google Closure I18N library], to generate its own I18N files. This means that
any changes to these files would be lost the next time that we import the library. The recommended
approach is to submit a patch to the I18N project directly, instead of submitting it here.*
**Please see the Submission Guidelines below**.
## Want a Feature?
You can request a new feature by submitting an issue to our [GitHub Repository][github]. If you
would like to implement a new feature then consider what kind of change it is:
* **Major Changes** that you wish to contribute to the project should be discussed first on our
[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.
## Want a Doc Fix?
If you want to help improve the docs, it's a good idea to let others know what you're working on to
minimize duplication of effort. Before starting, check out the issue queue for [Milestone:Docs Only](https://github.com/angular/angular.js/issues?milestone=24&state=open).
Comment on an issue to let others know what you're working on, or create a new issue if your work
doesn't fit within the scope of any of the existing doc fix projects.
For large fixes, please build and test the documentation before submitting the PR to be sure you haven't
accidentally introduced any layout or formatting issues.You should also make sure that your commit message
is labeled "docs:" and follows the **Git Commit Guidelines** outlined below.
If you're just making a small change, don't worry about filing an issue first. Use the friendly blue "Improve this doc" button at the top right of the doc page to fork the repository in-place and make a quick change on the fly.
## Submission Guidelines
### Submitting an Issue
Before you submit your issue search the archive, maybe your question was already answered.
* Search the archive first, it's likely that your question was already answered.
* A live example demonstrating your problem or question, will get an answer faster.
* Create one using this [template][template]
* If you get help, help others. Good karma rulez!
If your issue appears to be a bug, and hasn't been reported, open a new issue.
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:
features, by not reporting duplicate issues.
* **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
[JSFiddle][jsfiddle]) or a unambiguous set of steps.
* **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)
Here is a great example of a well defined issue: https://github.com/angular/angular.js/issues/5069
**If you get help, help others. Good karma rulez!**
### Submitting a Pull Request
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
requests. We cannot accept code without this.
* Make your changes in a new git branch
```shell
git checkout -b my-fix-branch master
```
* Create your patch, including appropriate test cases.
* Follow our [Coding Rules](#coding-rules)
* Commit your changes and create a descriptive commit message (the
commit message is used to generate release notes, please check out our
[commit message conventions](#commit-message-format) and our commit message presubmit hook
`validate-commit-msg.js`):
```shell
git commit -a
```
* Build your changes locally to ensure all the tests pass
```shell
grunt test
```
* Push your branch to Github:
```shell
git push origin my-fix-branch
```
* In Github, send a pull request to `angular:master`.
* If we suggest changes then you can modify your branch, rebase and force a new push to your GitHub
repository to update the Pull Request:
```shell
git rebase master -i
git push -f
```
That's it! Thank you for your contribution!
When the patch is reviewed and merged, you can safely delete your branch and pull the changes
from the main (upstream) repository:
* Delete the remote branch on Github:
```shell
git push origin --delete my-fix-branch
```
* Check out the master branch:
```shell
git checkout master -f
```
* Delete the local branch:
```shell
git branch -D my-fix-branch
```
* Update your master with the latest upstream version:
```shell
git pull --ff upstream master
```
### GitHub Pull Request Helper
We track Pull Requests by attaching labels and assigning to milestones. For some reason GitHub
does not provide a good UI for managing labels on Pull Requests (unlike Issues). We have developed
a simple Chrome Extension that enables you to view (and manage if you have permission) the labels
on Pull Requests. You can get the extension from the Chrome WebStore -
[GitHub PR Helper][github-pr-helper]
## Coding Rules
To ensure consistency throughout the source code, keep these rules in mind as you are working:
* All features or bug fixes **must be tested** by one or more [specs][unit-testing].
* All public API methods **must be documented** with ngdoc, an extended version of jsdoc (we added
support for markdown and templating via @ngdoc tag). To see how we document our APIs, please check
out the existing ngdocs and see [this wiki page][ngDocs].
* With the exceptions listed below, we follow the rules contained in
[Google's JavaScript Style Guide][js-style-guide]:
* **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 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
external API. See our existing code to see what we mean.
* We **don't go crazy with type annotations** for private internal APIs unless it's an internal API
that is used throughout AngularJS. The best guidance is to do what makes the most sense.
## Git Commit Guidelines
We have very precise rules over how our git commit messages can be formatted. This leads to **more
readable messages** that are easy to follow when looking through the **project history**. But also,
we use the git commit messages to **generate the AngularJS change log**.
### Commit Message Format
Each commit message consists of a **header**, a **body** and a **footer**. The header has a special
format that includes a **type**, a **scope** and a **subject**:
```
<type>(<scope>): <subject>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>
```
Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
to read on github as well as in various git tools.
### Type
Must be one of the following:
* **feat**: A new feature
* **fix**: A bug fix
* **docs**: Documentation only changes
* **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing
semi-colons, etc)
* **refactor**: A code change that neither fixes a bug or adds a feature
* **perf**: A code change that improves performance
* **test**: Adding missing tests
* **chore**: Changes to the build process or auxiliary tools and libraries such as documentation
generation
### Scope
The scope could be anything specifying place of the commit change. For example `$location`,
`$browser`, `$compile`, `$rootScope`, `ngHref`, `ngClick`, `ngView`, etc...
### Subject
The subject contains succinct description of the change:
* use the imperative, present tense: "change" not "changed" nor "changes"
* don't capitalize first letter
* no dot (.) at the end
###Body
Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes"
The body should include the motivation for the change and contrast this with previous behavior.
###Footer
The footer should contain any information about **Breaking Changes** and is also the place to
reference GitHub issues that this commit **Closes**.
A detailed explanation can be found in this [document][commit-message-format].
## 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!
* For individuals we have a [simple click-through form][individual-cla].
* For corporations we'll need you to
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
## Further Information
You can find out more detailed information about contributing in the
[AngularJS documentation][contributing].
[github]: https://github.com/angular/angular.js
[Google Closure I18N library]: https://code.google.com/p/closure-library/source/browse/closure/goog/i18n/
[list]: https://groups.google.com/forum/?fromgroups#!forum/angular
[contribute]: http://docs.angularjs.org/misc/contribute
[stackoverflow]: http://stackoverflow.com/questions/tagged/angularjs
[groups]: https://groups.google.com/forum/?fromgroups#!forum/angular
[angular-dev]: https://groups.google.com/forum/?fromgroups#!forum/angular-dev
[irc]: http://webchat.freenode.net/?channels=angularjs&uio=d4
[plunker]: http://plnkr.co/edit
[jsfiddle]: http://jsfiddle.net/
[ngDocs]: https://github.com/angular/angular.js/wiki/Writing-AngularJS-Documentation
[unit-testing]: http://docs.angularjs.org/guide/dev_guide.unit-testing
[js-style-guide]: http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
[contributing]: http://docs.angularjs.org/misc/contribute
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
[github-pr-helper]: https://chrome.google.com/webstore/detail/github-pr-helper/mokbklfnaddkkbolfldepnkfmanfhpen
[template]: http://plnkr.co/edit/gist:3510140
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/CONTRIBUTING.md?pixel)](https://github.com/igrigorik/ga-beacon)
## Contributing to Source Code
We'd love for you to contribute to our source code and to make AngularJS even
better than it is today!
Please read the [contribution guidelines][contribute] to learn about how to submit code as well as
other useful info like how to build and test AngularJS code.
[list]: https://groups.google.com/forum/?fromgroups#!forum/angular
[contribute]: http://docs.angularjs.org/misc/contribute

View file

@ -1,11 +1,12 @@
var files = require('./angularFiles').files;
var util = require('./lib/grunt/utils.js');
var path = require('path');
module.exports = function(grunt) {
//grunt plugins
require('load-grunt-tasks')(grunt);
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-compress');
grunt.loadTasks('lib/grunt');
var NG_VERSION = util.getVersion();
@ -20,15 +21,6 @@ module.exports = function(grunt) {
grunt.initConfig({
NG_VERSION: NG_VERSION,
parallel: {
travis: {
tasks: [
util.parallelTask(['test:unit', 'test:docgen', 'test:promises-aplus', 'tests:docs'], {stream: true}),
util.parallelTask(['test:e2e'])
]
}
},
connect: {
devserver: {
options: {
@ -48,106 +40,33 @@ module.exports = function(grunt) {
}
}
},
testserver: {
options: {
// We use end2end task (which does not start the webserver)
// and start the webserver as a separate process (in travis_build.sh)
// to avoid https://github.com/joyent/libuv/issues/826
port: 8000,
hostname: '0.0.0.0',
middleware: function(connect, options){
return [
function(req, resp, next) {
// cache get requests to speed up tests on travis
if (req.method === 'GET') {
resp.setHeader('Cache-control', 'public, max-age=3600');
}
next();
},
connect.favicon('images/favicon.ico'),
connect.static(options.base)
];
}
}
}
testserver: {}
},
tests: {
test: {
jqlite: 'karma-jqlite.conf.js',
jquery: 'karma-jquery.conf.js',
docs: 'karma-docs.conf.js',
modules: 'karma-modules.conf.js'
modules: 'karma-modules.conf.js',
//NOTE run grunt test:e2e instead and it will start a webserver for you
end2end: 'karma-e2e.conf.js'
},
autotest: {
jqlite: 'karma-jqlite.conf.js',
jquery: 'karma-jquery.conf.js',
modules: 'karma-modules.conf.js',
docs: 'karma-docs.conf.js'
jquery: 'karma-jquery.conf.js'
},
runprotractor: {
normal: 'protractor-conf.js'
},
clean: {build: ['build']},
clean: {
build: ['build'],
tmp: ['tmp']
},
jshint: {
options: {
jshintrc: true,
},
ng: {
files: { src: files['angularSrc'] },
},
ngAnimate: {
files: { src: 'src/ngAnimate/**/*.js' },
},
ngCookies: {
files: { src: 'src/ngCookies/**/*.js' },
},
ngLocale: {
files: { src: 'src/ngLocale/**/*.js' },
},
ngMock: {
files: { src: 'src/ngMock/**/*.js' },
},
ngResource: {
files: { src: 'src/ngResource/**/*.js' },
},
ngRoute: {
files: { src: 'src/ngRoute/**/*.js' },
},
ngSanitize: {
files: { src: 'src/ngSanitize/**/*.js' },
},
ngScenario: {
files: { src: 'src/ngScenario/**/*.js' },
},
ngTouch: {
files: { src: 'src/ngTouch/**/*.js' },
}
},
jscs: {
src: ['src/**/*.js', 'test/**/*.js'],
options: {
config: ".jscs.json"
}
},
build: {
scenario: {
dest: 'build/angular-scenario.js',
src: [
'bower_components/jquery/jquery.js',
'lib/jquery/jquery.js',
util.wrap([files['angularSrc'], files['angularScenario']], 'ngScenario/angular')
],
styles: {
@ -159,59 +78,65 @@ module.exports = function(grunt) {
src: util.wrap([files['angularSrc']], 'angular'),
styles: {
css: ['css/angular.css'],
generateCspCssFile: true,
minify: true
}
},
loader: {
dest: 'build/angular-loader.js',
src: util.wrap(files['angularLoader'], 'loader')
src: util.wrap(['src/loader.js'], 'loader')
},
touch: {
dest: 'build/angular-touch.js',
src: util.wrap(files['angularModules']['ngTouch'], 'module')
mobile: {
dest: 'build/angular-mobile.js',
src: util.wrap([
'src/ngMobile/mobile.js',
'src/ngMobile/directive/ngClick.js'
], 'module')
},
mocks: {
dest: 'build/angular-mocks.js',
src: util.wrap(files['angularModules']['ngMock'], 'module'),
src: ['src/ngMock/angular-mocks.js'],
strict: false
},
sanitize: {
dest: 'build/angular-sanitize.js',
src: util.wrap(files['angularModules']['ngSanitize'], 'module')
src: util.wrap([
'src/ngSanitize/sanitize.js',
'src/ngSanitize/directive/ngBindHtml.js',
'src/ngSanitize/filter/linky.js',
], 'module')
},
resource: {
dest: 'build/angular-resource.js',
src: util.wrap(files['angularModules']['ngResource'], 'module')
},
animate: {
dest: 'build/angular-animate.js',
src: util.wrap(files['angularModules']['ngAnimate'], 'module')
},
route: {
dest: 'build/angular-route.js',
src: util.wrap(files['angularModules']['ngRoute'], 'module')
src: util.wrap(['src/ngResource/resource.js'], 'module')
},
cookies: {
dest: 'build/angular-cookies.js',
src: util.wrap(files['angularModules']['ngCookies'], 'module')
src: util.wrap(['src/ngCookies/cookies.js'], 'module')
},
"promises-aplus-adapter": {
dest:'tmp/promises-aplus-adapter++.js',
src:['src/ng/q.js','lib/promises-aplus/promises-aplus-test-adapter.js']
bootstrap: {
dest: 'build/angular-bootstrap.js',
src: util.wrap(['src/bootstrap/bootstrap.js'], 'module')
},
bootstrapPrettify: {
dest: 'build/angular-bootstrap-prettify.js',
src: util.wrap(['src/bootstrap/bootstrap-prettify.js', 'src/bootstrap/google-prettify/prettify.js'], 'module'),
styles: {
css: ['src/bootstrap/google-prettify/prettify.css'],
minify: true
}
}
},
min: {
angular: 'build/angular.js',
animate: 'build/angular-animate.js',
cookies: 'build/angular-cookies.js',
loader: 'build/angular-loader.js',
touch: 'build/angular-touch.js',
mobile: 'build/angular-mobile.js',
resource: 'build/angular-resource.js',
route: 'build/angular-route.js',
sanitize: 'build/angular-sanitize.js'
sanitize: 'build/angular-sanitize.js',
bootstrap: 'build/angular-bootstrap.js',
bootstrapPrettify: 'build/angular-bootstrap-prettify.js',
},
@ -219,25 +144,6 @@ module.exports = function(grunt) {
process: ['build/docs/*.html', 'build/docs/.htaccess']
},
"jasmine_node": {
projectRoot: 'docs/spec'
},
"ddescribe-iit": {
files: [
'test/**/*.js',
'!test/ngScenario/DescribeSpec.js'
]
},
"merge-conflict": {
files: [
'src/**/*',
'test/**/*',
'docs/**/*',
'css/**/*'
]
},
copy: {
i18n: {
@ -250,55 +156,24 @@ module.exports = function(grunt) {
compress: {
build: {
options: {archive: 'build/' + dist +'.zip', mode: 'zip'},
options: {archive: 'build/' + dist +'.zip'},
src: ['**'], cwd: 'build', expand: true, dot: true, dest: dist + '/'
}
},
shell:{
"promises-aplus-tests":{
options:{
//stdout:true,
stderr:true,
failOnError:true
},
command:path.normalize('./node_modules/.bin/promises-aplus-tests tmp/promises-aplus-adapter++.js')
}
},
write: {
versionTXT: {file: 'build/version.txt', val: NG_VERSION.full},
versionJSON: {file: 'build/version.json', val: JSON.stringify(NG_VERSION)}
},
bump: {
options: {
files: ['package.json'],
commit: false,
createTag: false,
push: false
}
}
});
//alias tasks
grunt.registerTask('test', 'Run unit, docs and e2e tests with Karma', ['jshint', 'package','test:unit','test:promises-aplus', 'tests:docs', 'test:protractor']);
grunt.registerTask('test:jqlite', 'Run the unit tests with Karma' , ['tests:jqlite']);
grunt.registerTask('test:jquery', 'Run the jQuery unit tests with Karma', ['tests:jquery']);
grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', ['tests:modules']);
grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']);
grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', ['tests:jqlite', 'tests:jquery', 'tests:modules']);
grunt.registerTask('test:protractor', 'Run the end to end tests with Protractor and keep a test server running in the background', ['webdriver', 'connect:testserver', 'runprotractor:normal']);
grunt.registerTask('test:e2e', 'Alias for test:protractor', ['test:protractor']);
grunt.registerTask('test:docgen', ['jasmine_node']);
grunt.registerTask('test:promises-aplus',['build:promises-aplus-adapter','shell:promises-aplus-tests']);
grunt.registerTask('minify', ['bower','clean', 'build', 'minall']);
grunt.registerTask('test:unit', ['test:jqlite', 'test:jquery', 'test:modules']);
grunt.registerTask('minify', ['clean', 'build', 'minall']);
grunt.registerTask('test:e2e', ['connect:testserver', 'test:end2end']);
grunt.registerTask('webserver', ['connect:devserver']);
grunt.registerTask('package', ['bower','clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('package-without-bower', ['clean', 'buildall', 'minall', 'collect-errors', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('ci-checks', ['ddescribe-iit', 'merge-conflict', 'jshint', 'jscs']);
grunt.registerTask('package', ['clean', 'buildall', 'minall', 'docs', 'copy', 'write', 'compress']);
grunt.registerTask('default', ['package']);
};

View file

@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -1,23 +0,0 @@
Using AngularJS with the Closure Compiler
=========================================
The Closure Compiler project contains externs definitions for AngularJS
JavaScript in its `contrib/externs` directory.
The definitions contain externs for use with the Closure compiler (aka
JSCompiler). Passing these files to the --externs parameter of a compiler
pass allows using type annotations for AngularJS objects. For example,
Angular's $scope objects can be annotated as:
```js
/** @type {angular.Scope} */
var scope = $scope;
```
This allows JSCompiler to type check accesses to scope, give warnings about
missing methods or incorrect arguments, and also prevents renaming of property
accesses with advanced compilation.
The externs are incomplete and maintained on an as-needed basis, but strive to
be correct. Externs for individual modules should be added in separate files.
See https://developers.google.com/closure/compiler/

View file

@ -1,4 +1,4 @@
AngularJS [![Build Status](https://travis-ci.org/angular/angular.js.png?branch=master)](https://travis-ci.org/angular/angular.js)
AngularJS
=========
AngularJS lets you write client-side web applications as if you had a smarter browser. It lets you
@ -7,7 +7,7 @@ syntax to express your applications components clearly and succinctly. It au
synchronizes data from your UI (view) with your JavaScript objects (model) through 2-way data
binding. To help you structure your application better and make it easy to test, AngularJS teaches
the browser how to do dependency injection and inversion of control. Oh yeah and it also helps with
server-side communication, taming async callbacks with promises and deferreds; and makes client-side
server-side communication, taming async callbacks with promises and deferreds; and make client-side
navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all:
it makes development fun!
@ -16,7 +16,6 @@ it makes development fun!
* API Docs: http://docs.angularjs.org/api
* Developer Guide: http://docs.angularjs.org/guide
* Contribution guidelines: http://docs.angularjs.org/misc/contribute
* Dashboard: http://dashboard.angularjs.org
Building AngularJS
---------
@ -38,7 +37,3 @@ To execute end-to-end (e2e) tests, use:
To learn more about the grunt tasks, run `grunt --help` and also read our
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/README.md?pixel)](https://github.com/igrigorik/ga-beacon)

View file

@ -1,63 +0,0 @@
# Triage new issues/PRs on github
This document shows the steps the Angular team is using to triage issues.
The labels are used later on for planning releases.
## Tips ##
* install [github pr helper extension](https://github.com/petebacondarwin/github-pr-helper) and become 356% more productive
* Label "resolution:*"
* these tags can be used for labeling a closed issue/PR with a reason why it was closed. (we can add reasons as we need them, right there are only a few rejection reasons. it doesn't make sense to label issues that were fixed or prs that were merged)
## Automatic processing ##
We have automatic tools (e.g. Mary Poppins) that automatically add comments / labels to issues and PRs.
The following is done automatically and should not be done manually:
* Label "cla: yes" or "cla: no" for pull requests
## Process ##
1. Open list of [non triaged issues](https://github.com/angular/angular.js/issues?direction=desc&milestone=none&page=1&sort=created&state=open)
1. Assign yourself: Pick an issue that is not assigned to anyone and assign it to you
1. Assign milestone:
* "Docs only" milestone - for documentation PR -> **Done**.
* Current/next milestone - regressions
* 1.2.x - everything else
1. Label "GH: *" (to be automated via Mary Poppins)
* PR - issue is a PR
* issue - otherwise
1. Bugs:
* Label "Type: Bug"
* Label "Type: Regression" - if the bug is a regression
* Duplicate? - Check if there are comments pointing out that this is a dupe, if they do exist verify that this is indeed a dupe and close it and go to the last step
* Reproducible? - Steps to reproduce the bug are clear, if not ask for clarification (ideally plunker or fiddle)
* Reproducible on master? - http://code.angularjs.org/snapshot/
1. Non bugs:
* Label "Type: Feature" or "Type: Chore" or "Type: Perf"
* Label "needs: breaking change" - if needed
* Label "needs: public api" - if a new public api is needed
* Understandable? - verify if the description of the request is clear. if not ask for clarification
* Goals of angular core? - Often new features should be implemented as a third-party module rather than an addition to the core.
1. Label "component: *"
* In rare cases, it's ok to have multiple components.
1. Label "impact: *"
* small - obscure issue affecting one or handful of developers
* medium - impacts some usage patterns
* large - impacts most or all of angular apps
1. Label "complexity: *"
* small - trivial change
* medium - non-trivial but straightforward change
* large - changes to many components in angular or any changes to $compile, ngRepeat or other "fun" components
1. Label "PRs plz!" for "GH: issue"
* if complexity is small or medium and the problem as well as solution are well captured in the issue
1. Label "origin: google" for issues from Google
1. Label "high priority" for security issues, major performance regressions or memory leaks
1. Unassign yourself from the issue
[![Analytics](https://ga-beacon.appspot.com/UA-8594346-11/angular.js/TRIAGING.md?pixel)](https://github.com/igrigorik/ga-beacon)

186
angularFiles.js vendored Executable file → Normal file
View file

@ -1,6 +1,5 @@
angularFiles = {
'angularSrc': [
'src/minErr.js',
'src/Angular.js',
'src/loader.js',
'src/AngularPublic.js',
@ -10,29 +9,28 @@ angularFiles = {
'src/auto/injector.js',
'src/ng/anchorScroll.js',
'src/ng/animate.js',
'src/ng/animation.js',
'src/ng/animator.js',
'src/ng/browser.js',
'src/ng/cacheFactory.js',
'src/ng/compile.js',
'src/ng/controller.js',
'src/ng/document.js',
'src/ng/exceptionHandler.js',
'src/ng/http.js',
'src/ng/httpBackend.js',
'src/ng/interpolate.js',
'src/ng/interval.js',
'src/ng/locale.js',
'src/ng/location.js',
'src/ng/log.js',
'src/ng/parse.js',
'src/ng/q.js',
'src/ng/route.js',
'src/ng/routeParams.js',
'src/ng/rootScope.js',
'src/ng/sanitizeUri.js',
'src/ng/sce.js',
'src/ng/sniffer.js',
'src/ng/timeout.js',
'src/ng/urlUtils.js',
'src/ng/window.js',
'src/ng/http.js',
'src/ng/httpBackend.js',
'src/ng/locale.js',
'src/ng/timeout.js',
'src/ng/filter.js',
'src/ng/filter/filter.js',
@ -51,7 +49,6 @@ angularFiles = {
'src/ng/directive/ngController.js',
'src/ng/directive/ngCsp.js',
'src/ng/directive/ngEventDirs.js',
'src/ng/directive/ngIf.js',
'src/ng/directive/ngInclude.js',
'src/ng/directive/ngInit.js',
'src/ng/directive/ngNonBindable.js',
@ -61,49 +58,26 @@ angularFiles = {
'src/ng/directive/ngStyle.js',
'src/ng/directive/ngSwitch.js',
'src/ng/directive/ngTransclude.js',
'src/ng/directive/ngView.js',
'src/ng/directive/script.js',
'src/ng/directive/select.js',
'src/ng/directive/style.js'
],
'angularLoader': [
'src/minErr.js',
'src/loader.js'
'angularSrcModules': [
'src/ngCookies/cookies.js',
'src/ngResource/resource.js',
'src/ngSanitize/sanitize.js',
'src/ngSanitize/directive/ngBindHtml.js',
'src/ngSanitize/filter/linky.js',
'src/ngMock/angular-mocks.js',
'src/ngMobile/mobile.js',
'src/ngMobile/directive/ngClick.js',
'src/bootstrap/bootstrap.js'
],
'angularModules': {
'ngAnimate': [
'src/ngAnimate/animate.js'
],
'ngCookies': [
'src/ngCookies/cookies.js'
],
'ngResource': [
'src/ngResource/resource.js'
],
'ngRoute': [
'src/ngRoute/route.js',
'src/ngRoute/routeParams.js',
'src/ngRoute/directive/ngView.js'
],
'ngSanitize': [
'src/ngSanitize/sanitize.js',
'src/ngSanitize/filter/linky.js'
],
'ngMock': [
'src/ngMock/angular-mocks.js'
],
'ngTouch': [
'src/ngTouch/touch.js',
'src/ngTouch/swipe.js',
'src/ngTouch/directive/ngClick.js',
'src/ngTouch/directive/ngSwipe.js'
],
},
'angularScenario': [
'src/ngScenario/Scenario.js',
'src/ngScenario/browserTrigger.js',
'src/ngScenario/Application.js',
'src/ngScenario/Describe.js',
'src/ngScenario/Future.js',
@ -119,104 +93,140 @@ angularFiles = {
],
'angularTest': [
'test/helpers/*.js',
'test/testabilityPatch.js',
'test/matchers.js',
'test/ngScenario/*.js',
'test/ngScenario/output/*.js',
'test/ngScenario/jstd-scenario-adapter/*.js',
'test/*.js',
'test/auto/*.js',
'test/ng/**/*.js',
'test/ngAnimate/*.js',
'test/bootstrap/*.js',
'test/ng/*.js',
'test/ng/directive/*.js',
'test/ng/filter/*.js',
'test/ngCookies/*.js',
'test/ngResource/*.js',
'test/ngRoute/**/*.js',
'test/ngSanitize/**/*.js',
'test/ngSanitize/*.js',
'test/ngSanitize/directive/*.js',
'test/ngSanitize/filter/*.js',
'test/ngMock/*.js',
'test/ngTouch/**/*.js'
'test/ngMobile/directive/*.js'
],
'karma': [
'bower_components/jquery/jquery.js',
'jstd': [
'lib/jasmine/jasmine.js',
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
'lib/jquery/jquery.js',
'test/jquery_remove.js',
'@angularSrc',
'src/publishExternalApis.js',
'@angularSrcModules',
'@angularScenario',
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
'@angularTest',
'example/personalLog/*.js',
'example/personalLog/test/*.js'
],
'karmaExclude': [
'jstdExclude': [
'test/jquery_alias.js',
'src/angular-bootstrap.js',
'src/ngScenario/angular-bootstrap.js'
],
'karmaScenario': [
'jstdScenario': [
'build/angular-scenario.js',
'build/jstd-scenario-adapter-config.js',
'build/jstd-scenario-adapter.js',
'build/docs/docs-scenario.js'
],
"karmaModules": [
"jstdModules": [
'lib/jasmine/jasmine.js',
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
'build/angular.js',
'@angularSrcModules',
'src/ngScenario/browserTrigger.js',
'test/helpers/*.js',
'build/angular-scenario.js',
'src/ngMock/angular-mocks.js',
'src/ngCookies/cookies.js',
'src/ngResource/resource.js',
'src/ngMobile/mobile.js',
'src/ngMobile/directive/ngClick.js',
'src/ngSanitize/sanitize.js',
'src/ngSanitize/directive/ngBindHtml.js',
'src/ngSanitize/filter/linky.js',
'test/matchers.js',
'test/ngMock/*.js',
'test/ngCookies/*.js',
'test/ngRoute/**/*.js',
'test/ngResource/*.js',
'test/ngSanitize/**/*.js',
'test/ngTouch/**/*.js'
'test/ngSanitize/*.js',
'test/ngSanitize/directive/*.js',
'test/ngSanitize/filter/*.js',
'test/ngMobile/directive/*.js'
],
'karmaJquery': [
'bower_components/jquery/jquery.js',
'jstdPerf': [
'lib/jasmine/jasmine.js',
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
'@angularSrc',
'@angularSrcModules',
'src/ngMock/angular-mocks.js',
'perf/data/*.js',
'perf/testUtils.js',
'perf/*.js'
],
'jstdPerfExclude': [
'src/ng/angular-bootstrap.js',
'src/ngScenario/angular-bootstrap.js'
],
'jstdJquery': [
'lib/jasmine/jasmine.js',
'lib/jasmine-jstd-adapter/JasmineAdapter.js',
'lib/jquery/jquery.js',
'test/jquery_alias.js',
'@angularSrc',
'src/publishExternalApis.js',
'@angularSrcModules',
'@angularScenario',
'src/ngScenario/jstd-scenario-adapter/Adapter.js',
'@angularTest',
'example/personalLog/*.js',
'example/personalLog/test/*.js'
],
'karmaJqueryExclude': [
'jstdJqueryExclude': [
'src/angular-bootstrap.js',
'src/ngScenario/angular-bootstrap.js',
'test/jquery_remove.js'
]
};
angularFiles['angularSrcModules'] = [].concat(
angularFiles['angularModules']['ngAnimate'],
angularFiles['angularModules']['ngCookies'],
angularFiles['angularModules']['ngResource'],
angularFiles['angularModules']['ngRoute'],
angularFiles['angularModules']['ngSanitize'],
angularFiles['angularModules']['ngMock'],
angularFiles['angularModules']['ngTouch']
);
if (exports) {
exports.files = angularFiles;
exports.mergeFilesFor = function() {
exports.files = angularFiles
exports.mergeFiles = function mergeFiles() {
var files = [];
Array.prototype.slice.call(arguments, 0).forEach(function(filegroup) {
angularFiles[filegroup].forEach(function(file) {
// replace @ref
var match = file.match(/^\@(.*)/);
if (match) {
files = files.concat(angularFiles[match[1]]);
} else {
files.push(file);
}
});
[].splice.call(arguments, 0).forEach(function(file) {
if (file.match(/karma/)) {
files.push(file);
} else {
angularFiles[file].forEach(function(f) {
// replace @ref
var match = f.match(/^\@(.*)/);
if (match) {
var deps = angularFiles[match[1]];
files = files.concat(deps);
} else {
if (!/jstd|jasmine/.test(f)) { //TODO(i): remove once we don't have jstd/jasmine in repo
files.push(f);
}
}
});
}
});
return files;
};
}
}

View file

@ -1,12 +0,0 @@
{
"name": "AngularJS",
"devDependencies": {
"jquery": "1.10.2",
"lunr.js": "0.4.0",
"google-code-prettify": "1.0.0",
"components-font-awesome": "3.1.0",
"bootstrap": "https://raw.github.com/twbs/bootstrap/v2.0.2/docs/assets/bootstrap.zip",
"closure-compiler": "https://closure-compiler.googlecode.com/files/compiler-20130603.zip",
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.2/assets/ng-closure-runner.zip"
}
}

View file

@ -16,6 +16,7 @@ var LINK_ISSUE = '[#%s](https://github.com/angular/angular.js/issues/%s)';
var LINK_COMMIT = '[%s](https://github.com/angular/angular.js/commit/%s)';
var EMPTY_COMPONENT = '$$';
var MAX_SUBJECT_LENGTH = 80;
var warn = function() {
@ -53,6 +54,11 @@ var parseRawCommit = function(raw) {
return null;
}
if (match[3].length > MAX_SUBJECT_LENGTH) {
warn('Too long subject: %s %s', msg.hash, msg.subject);
match[3] = match[3].substr(0, MAX_SUBJECT_LENGTH);
}
msg.type = match[1];
msg.component = match[2];
msg.subject = match[3];
@ -142,7 +148,6 @@ var writeChangelog = function(stream, commits, version) {
var sections = {
fix: {},
feat: {},
perf: {},
breaks: {}
};
@ -170,7 +175,6 @@ var writeChangelog = function(stream, commits, version) {
stream.write(util.format(HEADER_TPL, version, version, currentDate()));
printSection(stream, 'Bug Fixes', sections.fix);
printSection(stream, 'Features', sections.feat);
printSection(stream, 'Performance Improvements', sections.perf);
printSection(stream, 'Breaking Changes', sections.breaks, false);
}
@ -188,7 +192,7 @@ 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) {
readGitLog('^fix|^feat|Breaks', tag).then(function(commits) {
console.log('Parsed', commits.length, 'commits');
console.log('Generating changelog to', file || 'stdout', '(', version, ')');
writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);

80
changelog.tmp.md Normal file
View file

@ -0,0 +1,80 @@
<a name="v1.0.0rc3"></a>
# v1.0.0rc3 (2012-03-27)
## Bug Fixes
- **$compile:**
- create new (isolate) scopes for directives on root elements ([5390fb37](https://github.com/angular/angular.js/commit/5390fb37d2c01937922613fc57df4986af521787), closes [#817](https://github.com/angular/angular.js/issues/817))
- don't touch static element attributes ([9cb2195e](https://github.com/angular/angular.js/commit/9cb2195e61a78e99020ec19d687a221ca88b5900))
- Merge interpolated css class when replacing an element ([f49eaf8b](https://github.com/angular/angular.js/commit/f49eaf8bf2df5f4e0e82d6c89e849a4f82c8d414))
- **$http:**
- don't send Content-Type header when no data ([1a5bebd9](https://github.com/angular/angular.js/commit/1a5bebd927ecd22f9c34617642fdf58fe3f62efb), closes [#749](https://github.com/angular/angular.js/issues/749))
- **$log:**
- avoid console.log.apply calls in IE ([15213ec2](https://github.com/angular/angular.js/commit/15213ec212769837cb2b7e781ffc5bfd598d27ca), closes [#805](https://github.com/angular/angular.js/issues/805))
- **$resource:**
- support escaping of ':' in resource url ([6d6f8753](https://github.com/angular/angular.js/commit/6d6f875345e01f2c6c63ef95164f6f39e923da15))
- **compiler:**
- allow transclusion of root elements ([9918b748](https://github.com/angular/angular.js/commit/9918b748be01266eb10db39d51b4d3098d54ab66))
- **e2e runner:**
- fix typo that caused errors on IE8 ([ee5a5352](https://github.com/angular/angular.js/commit/ee5a5352fd4b94cedee6ef20d4bf2d43ce77e00b), closes [#806](https://github.com/angular/angular.js/issues/806))
- **forEach:**
- should ignore prototypically inherited properties ([8d7e6948](https://github.com/angular/angular.js/commit/8d7e6948496ff26ef1da8854ba02fcb8eebfed61), closes [#813](https://github.com/angular/angular.js/issues/813))
- **forms:**
- Remove double registering of form ([1faafa31](https://github.com/angular/angular.js/commit/1faafa31582c4e9413f48dc7d12f5b681f9fe9fd))
- Set ng-valid/ng-invalid correctly ([08bfea18](https://github.com/angular/angular.js/commit/08bfea183a850b29da270eac47f80b598cbe600f))
- **init:**
- use jQuery#ready for init if available ([cb2ad9ab](https://github.com/angular/angular.js/commit/cb2ad9abf24e6f855cc749efe3155bd7987ece9d), closes [#818](https://github.com/angular/angular.js/issues/818))
- **json:**
- added support for iso8061 timezone ([5ac14f63](https://github.com/angular/angular.js/commit/5ac14f633a69f49973b5512780c6ec7752405967))
- **matchers.toHaveClass:**
- Correct reference to angular.mock.dump ([f701ce08](https://github.com/angular/angular.js/commit/f701ce08f9d63be05fc3b92f57ad473e1e749b2d))
- **ng-switch:**
- properly destroy child scopes ([2315d9b3](https://github.com/angular/angular.js/commit/2315d9b3610994b36c44e4a97fb1427d59471ce8))
- **ngDocSpec:**
- fix broken tests ([53b6f522](https://github.com/angular/angular.js/commit/53b6f522a56eea314cbd084816e08f24b2c7879f))
- **ngForm:**
- alias name||ngForm ([823adb23](https://github.com/angular/angular.js/commit/823adb231995e917bc060bfa49453e2a96bac2b6))
- **ngRepeat:**
- correct variable reference in error message ([935c1018](https://github.com/angular/angular.js/commit/935c1018da05dbf3124b2dd33619c4a3c82d7a2a))
- **ngView:**
- controller not published ([21e74c2d](https://github.com/angular/angular.js/commit/21e74c2d2e8e985b23711785287feb59965cbd90))
- **q:**
- resolve all of nothing to nothing ([ac75079e](https://github.com/angular/angular.js/commit/ac75079e2113949d5d64adbcf23d56f3cf295d41))
- **select:**
- multiselect failes to update view on selection insert ([6ecac8e7](https://github.com/angular/angular.js/commit/6ecac8e71a84792a434d21db2c245b3648c55f18))
## Features
- **$compile:**
- do not interpolate boolean attributes, rather evaluate them ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
- **$controller:**
- support controller registration via $controllerProvider ([d54dfecb](https://github.com/angular/angular.js/commit/d54dfecb00fba41455536c5ddd55310592fdaf84))
- **$route:**
- when matching consider trailing slash as optional ([a4fe51da](https://github.com/angular/angular.js/commit/a4fe51da3ba0dc297ecd389e230d6664f250c9a6), closes [#784](https://github.com/angular/angular.js/issues/784))
- **assertArgFn:**
- should support array annotated fns ([4b8d9260](https://github.com/angular/angular.js/commit/4b8d926062eb4d4483555bdbdec4656f585ab40b))
- **http:**
- added params parameter ([73c85930](https://github.com/angular/angular.js/commit/73c8593077155a9f2e8ef42efd4c497eba0bef4f))
- **injector:**
- infer _foo_ as foo ([f13dd339](https://github.com/angular/angular.js/commit/f13dd3393dfb7a33565c9360342c193bc0bddcb6))
- **input.radio:**
- Allow value attribute to be interpolated ([ade6c452](https://github.com/angular/angular.js/commit/ade6c452753145c84884d17027a7865bf4b34b0c))
- **jqLite:**
- make injector() and scope() work with the document object ([5fdab52d](https://github.com/angular/angular.js/commit/5fdab52dd7c269f99839f4fa6b5854d9548269fa))
- add .controller() method ([6c5a05ad](https://github.com/angular/angular.js/commit/6c5a05ad49a1e083570c3dfe331403398f899dbe))
- **ngValue:**
- allow radio inputs to have non string values ([09e175f0](https://github.com/angular/angular.js/commit/09e175f02cca0f4a295fd0c9b980cd8f432e722b), closes [#816](https://github.com/angular/angular.js/issues/816))
- **scope:**
- broadcast $destroy event on scope destruction ([9b1aff90](https://github.com/angular/angular.js/commit/9b1aff905b638aa274a5fc8f88662df446d374bd))
- **scope.$eval:**
- Allow passing locals to the expression ([192ff61f](https://github.com/angular/angular.js/commit/192ff61f5d61899e667c6dbce4d3e6e399429d8b))
## Breaking Changes
- boolean attrs are evaluated rather than interpolated ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
- ng-bind-attr directive removed ([55027132](https://github.com/angular/angular.js/commit/55027132f3d57e5dcf94683e6e6bd7b0aae0087d))
- any app that depends on this service and its fallback to Modernizr, please ([aaedefb9](https://github.com/angular/angular.js/commit/aaedefb92e6bec6626e173e5155072c91471596a))

View file

@ -1,163 +0,0 @@
#!/usr/local/bin/node
var util = require('util');
var cp = require('child_process');
var Q = require('q');
var _ = require('lodash');
var semver = require('semver');
var exec = function (cmd) {
return function () {
var args = Array.prototype.slice.call(arguments, 0);
args.unshift(cmd);
var fullCmd = util.format.apply(util, args);
return Q.nfcall(cp.exec, fullCmd).then(function (out) {
return out[0].split('\n');
});
};
};
var andThen = function (fn, after) {
return function () {
return fn.apply(this, arguments).then(after);
};
};
var oneArg = function (fn) {
return function (arg) {
return fn(arg);
};
};
var oneLine = function (lines) {
return lines[0].trim();
};
var noArgs = function (fn) {
return function () {
return fn();
};
};
var identity = function (i) { return i; };
// like Q.all, but runs the comands in series
// useful for ensuring env state (like which branch is checked out)
var allInSeries = function (fn) {
return function (args) {
var results = [];
var def;
while (args.length > 0) {
(function (arg) {
if (def) {
def = def.then(function () {
return fn(arg);
});
} else {
def = fn(arg);
}
def = def.then(function (res) {
results.push(res);
});
}(args.pop()));
}
return def.then(function () {
return results;
});
};
};
var compareBranches = function (left, right) {
console.log('# These commits are in ' + left.name + ' but not in ' + right.name + '\n');
console.log(_(left.log).
difference(right.log).
map(function (line) {
return left.full[left.log.indexOf(line)]; // lol O(n^2)
}).
value().
join('\n'));
};
var checkout = oneArg(exec('git checkout %s'));
var getCurrentBranch = andThen(noArgs(exec('git rev-parse --abbrev-ref HEAD')), oneLine);
var getTags = noArgs(exec('git tag'));
var getShaOfTag = oneArg(exec('git rev-list %s | head -n 1'));
var getTheLog = oneArg(exec('git log --pretty=oneline %s..HEAD | cat'));
// remember this so we can restore state
var currentBranch;
getCurrentBranch().
then(function (branch) {
currentBranch = branch;
}).
then(getTags).
then(function (tags) {
return tags.
filter(semver.valid).
map(semver.clean).
sort(semver.rcompare);
}).
then(function (tags) {
var major = tags[0].split('.')[0] + '.x';
return tags.
filter(function (ver) {
return semver.satisfies(ver, major);
});
}).
then(function (tags) {
return _(tags).
groupBy(function (tag) {
return tag.split('.')[1];
}).
map(function (group) {
return _.first(group);
}).
map(function (tag) {
return 'v' + tag;
}).
value();
}).
then(function (tags) {
return [
{ name: 'v1.0.x', tag: tags[0] },
{ name: 'master', tag: tags[1] }
];
}).
then(allInSeries(function (branch) {
return checkout(branch.name).
then(function () {
return getTheLog(branch.tag);
}).
then(function (log) {
return log.
filter(identity);
}).
then(function (log) {
branch.full = log.map(function (line) {
line = line.split(' ');
var sha = line.shift();
var msg = line.join(' ');
return sha + (msg.toLowerCase().indexOf('fix') === -1 ? ' ' : ' * ') + msg;
});
branch.log = log.map(function (line) {
return line.substr(41)
});
return branch;
});
})).
then(function (pairs) {
compareBranches(pairs[0], pairs[1]);
console.log('\n');
compareBranches(pairs[1], pairs[0]);
return pairs;
}).
then(function () {
return checkout(currentBranch);
}).
catch(function (e) {
console.log(e.stack);
});

View file

@ -1,9 +1,8 @@
@charset "UTF-8";
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak],
.ng-cloak, .x-ng-cloak,
.ng-hide {
display: none !important;
.ng-cloak, .x-ng-cloak {
display: none;
}
ng\:form {

View file

@ -1,72 +0,0 @@
describe("DocsNavigationCtrl", function() {
beforeEach(module('docsApp'));
var ctrl, $scope;
beforeEach(function() {
module(function($provide) {
$provide.value('docsPages', []);
$provide.factory('docsSearch', function() {
return function(q) {
return ['one','two','three'];
};
});
});
inject(function($controller, $rootScope, $location, docsSearch) {
$scope = $rootScope.$new();
ctrl = $controller('DocsNavigationCtrl', {
$scope : $scope,
$location : $location,
docsSearch : docsSearch
});
});
});
it("should search and return data from docsSearch", function() {
$scope.search('1234')
expect($scope.results.join(',')).toBe('one,two,three');
expect($scope.hasResults).toBe(true);
});
it("should avoid searching if the search term is too short", function() {
$scope.search('1')
expect($scope.results.length).toBe(0);
expect($scope.hasResults).toBe(false);
});
it("should set the columns classname based on the total grouped results", function() {
$scope.search('1234');
expect($scope.colClassName).toBe('cols-3');
$scope.search('1');
expect($scope.colClassName).toBe(null);
});
it("should hide and clear the results when called", function() {
$scope.hasResults = true;
$scope.results = ['one'];
$scope.colClassName = '...';
$scope.hideResults();
expect($scope.hasResults).toBe(false);
expect($scope.results.length).toBe(0);
expect($scope.colClassName).toBe(null);
});
it("should hide, clear and change the path of the page when submitted", inject(function($location) {
$scope.hasResults = true;
$scope.results = {
api : [
{url : '/home'}
],
tutorial : [
{url : '/tutorial'}
]
};
$scope.submit();
expect($location.path()).toBe('/home');
expect($scope.results.length).toBe(0);
expect($scope.hasResults).toBe(false);
}));
});

View file

@ -1,195 +0,0 @@
describe('Docs Annotations', function() {
beforeEach(module('docsApp'));
var body;
beforeEach(function() {
body = angular.element(document.body);
body.empty();
});
var normalizeHtml = function(html) {
return html.toLowerCase().replace(/\s*$/, '');
};
describe('popover directive', function() {
var $scope, element;
beforeEach(inject(function($rootScope, $compile) {
$scope = $rootScope.$new();
element = angular.element(
'<div style="margin:200px;" data-title="title_text" data-content="content_text" popover></div>'
);
element.attr('id','idx');
body.append(element);
$compile(element)($scope);
$scope.$apply();
}));
it('should be hidden by default', inject(function(popoverElement) {
expect(popoverElement.visible()).toBe(false);
}));
it('should capture the click event and set the title and content and position the tip', inject(function(popoverElement) {
element.triggerHandler('click');
expect(popoverElement.isSituatedAt(element)).toBe(true);
expect(popoverElement.visible()).toBe(true);
expect(popoverElement.title()).toBe('title_text');
expect(popoverElement.content()).toContain('content_text');
expect(popoverElement.besideElement.attr('id')).toBe('idx');
}));
it('should hide and clear the title and content if the same element is clicked again', inject(function(popoverElement) {
//show the element
element.triggerHandler('click');
expect(popoverElement.isSituatedAt(element)).toBe(true);
//hide the element
element.triggerHandler('click');
expect(popoverElement.isSituatedAt(element)).toBe(false);
expect(popoverElement.visible()).toBe(false);
expect(popoverElement.title()).toBe('');
expect(popoverElement.content()).toBe('');
}));
it('should parse markdown content', inject(function(popoverElement, $compile) {
element = angular.element(
'<div style="margin:200px;" data-title="#title_text" data-content="#heading" popover></div>'
);
body.append(element);
$compile(element)($scope);
$scope.$apply();
element.triggerHandler('click');
expect(popoverElement.title()).toBe('#title_text');
expect(normalizeHtml(popoverElement.content())).toMatch('<h1>heading</h1>');
}));
});
describe('foldout directive', function() {
// Do not run this suite on Internet Explorer.
if (msie < 10) return;
var $scope, parent, element, url;
beforeEach(function() {
module(function($provide, $animateProvider) {
$animateProvider.register('.foldout', function($timeout) {
return {
enter : function(element, done) {
$timeout(done, 1000);
},
removeClass : function(element, className, done) {
$timeout(done, 500);
},
addClass : function(element, className, done) {
$timeout(done, 200);
}
}
});
});
inject(function($rootScope, $compile, $templateCache, $rootElement, $animate) {
$animate.enabled(true);
url = '/page.html';
$scope = $rootScope.$new();
parent = angular.element('<div class="parent"></div>');
//we're injecting the element to the $rootElement since the changes in
//$animate only detect and perform animations if the root element has
//animations enabled. If the element is not apart of the DOM
//then animations are skipped.
element = angular.element('<div data-url="' + url + '" class="foldout" foldout></div>');
parent.append(element);
$rootElement.append(parent);
body.append($rootElement);
$compile(parent)($scope);
$scope.$apply();
});
});
it('should inform that it is loading', inject(function($httpBackend) {
$httpBackend.expect('GET', url).respond('hello');
element.triggerHandler('click');
var kids = body.children();
var foldout = angular.element(kids[kids.length-1]);
expect(foldout.html()).toContain('loading');
}));
//TODO(matias): this test is bad. it's not clear what is being tested and what the assertions are.
// Additionally, now that promises get auto-flushed there are extra tasks in the deferred queue which screws up
// these brittle tests.
xit('should download a foldout HTML page and animate the contents', inject(function($httpBackend, $timeout, $sniffer) {
$httpBackend.expect('GET', url).respond('hello');
element.triggerHandler('click');
$httpBackend.flush();
$timeout.flushNext(0);
$timeout.flushNext(1000);
var kids = body.children();
var foldout = angular.element(kids[kids.length-1]);
expect(foldout.text()).toContain('hello');
}));
//TODO(matias): this test is bad. it's not clear what is being tested and what the assertions are.
// Additionally, now that promises get auto-flushed there are extra tasks in the deferred queue which screws up
// these brittle tests.
xit('should hide then show when clicked again', inject(function($httpBackend, $timeout, $sniffer) {
$httpBackend.expect('GET', url).respond('hello');
//enter
element.triggerHandler('click');
$httpBackend.flush();
$timeout.flushNext(0);
$timeout.flushNext(1000);
//hide
element.triggerHandler('click');
$timeout.flushNext(0);
$timeout.flushNext(200);
//show
element.triggerHandler('click');
$timeout.flushNext(0);
$timeout.flushNext(500);
$timeout.flushNext(0);
}));
});
describe('DocsController fold', function() {
var $scope, ctrl;
beforeEach(function() {
inject(function($rootScope, $controller, $location, $cookies, sections) {
$scope = $rootScope.$new();
ctrl = $controller('DocsController',{
$scope : $scope,
$location : $location,
$cookies : $cookies,
sections : sections
});
});
});
it('should download and reveal the foldover container', inject(function($compile, $httpBackend) {
var url = '/page.html';
var fullUrl = '/notes/' + url;
$httpBackend.expect('GET', fullUrl).respond('hello');
var element = angular.element('<div ng-include="docs_fold"></div>');
$compile(element)($scope);
$scope.$apply();
$scope.fold(url);
$httpBackend.flush();
}));
});
});

View file

@ -1,53 +0,0 @@
describe("docsSearch", function() {
beforeEach(module('docsApp'));
var interceptedLunrResults;
beforeEach(function() {
interceptedLunrResults = [];
});
beforeEach(function() {
module(function($provide) {
var results = [];
results[0] = { section: 'tutorial', shortName: 'item one', keywords: 'item, one, 1' };
results[1] = { section: 'tutorial', shortName: 'item man', keywords: 'item, man' };
results[2] = { section: 'api', shortName: 'item other', keywords: 'item, other' };
results[3] = { section: 'api', shortName: 'ngRepeat', keywords: 'item, other' };
$provide.value('NG_PAGES', results);
$provide.factory('lunrSearch', function() {
return function() {
return {
store : function(value) {
interceptedLunrResults.push(value);
},
search : function(q) {
var data = [];
angular.forEach(results, function(res, i) {
data.push({ ref : i });
});
return data;
}
}
};
});
});
});
it("should lookup and organize values properly", inject(function(docsSearch) {
var items = docsSearch('item');
expect(items['api'].length).toBe(2);
}));
it("should return all results without a search", inject(function(docsSearch) {
var items = docsSearch();
expect(items['tutorial'].length).toBe(2);
expect(items['api'].length).toBe(2);
}));
it("should store values with and without a ng prefix", inject(function(docsSearch) {
expect(interceptedLunrResults[3].title).toBe('ngRepeat repeat');
}));
});

View file

@ -1,76 +0,0 @@
describe("errorDisplay", function () {
var $location, compileHTML;
beforeEach(module('docsApp'));
beforeEach(inject(function ($injector) {
var $rootScope = $injector.get('$rootScope'),
$compile = $injector.get('$compile');
$location = $injector.get('$location');
compileHTML = function (code) {
var elm = angular.element(code);
$compile(elm)($rootScope);
$rootScope.$digest();
return elm;
};
this.addMatchers({
toInterpolateTo: function (expected) {
// Given a compiled DOM node with a minerr-display attribute,
// assert that its interpolated string matches the expected text.
return this.actual.text() === expected;
}
});
}));
it('should interpolate a template with no parameters', function () {
var elm;
spyOn($location, 'search').andReturn({});
elm = compileHTML('<div error-display="This is a test"></div>');
expect(elm).toInterpolateTo('This is a test');
});
it('should interpolate a template with no parameters when search parameters are present', function () {
var elm;
spyOn($location, 'search').andReturn({ p0: 'foobaz' });
elm = compileHTML('<div error-display="This is a test"></div>');
expect(elm).toInterpolateTo('This is a test');
});
it('should correctly interpolate search parameters', function () {
var elm;
spyOn($location, 'search').andReturn({ p0: '42' });
elm = compileHTML('<div error-display="The answer is {0}"></div>');
expect(elm).toInterpolateTo('The answer is 42');
});
it('should interpolate parameters in the specified order', function () {
var elm;
spyOn($location, 'search').andReturn({ p0: 'second', p1: 'first' });
elm = compileHTML('<div error-display="{1} {0}"></div>');
expect(elm).toInterpolateTo('first second');
});
it('should preserve interpolation markers when fewer arguments than needed are provided', function () {
var elm;
spyOn($location, 'search').andReturn({ p0: 'Fooooo' });
elm = compileHTML('<div error-display="This {0} is {1} on {2}"></div>');
expect(elm).toInterpolateTo('This Fooooo is {1} on {2}');
});
it('should correctly handle the empty string as an interpolation parameter', function () {
var elm;
spyOn($location, 'search').andReturn({ p0: 'test', p1: '' });
elm = compileHTML('<div error-display="This {0} is a {1}"></div>');
expect(elm).toInterpolateTo('This test is a ');
});
});

View file

@ -1,52 +0,0 @@
describe("errorLinkFilter", function () {
var errorLinkFilter;
beforeEach(module('docsApp'));
beforeEach(inject(function ($filter) {
errorLinkFilter = $filter('errorLink');
}));
it('should not change text that does not contain links', function () {
expect(errorLinkFilter('This is a test')).toBe('This is a test');
});
it('should find links in text and linkify them', function () {
var output = errorLinkFilter("http://ab/ (http://a/) http://1.2/v:~-123. c");
//temporary fix for IE8 sanitization whitespace bug
output = output.replace('</a>(','</a> (');
expect(output).
toBe('<a href="http://ab/">http://ab/</a> ' +
'(<a href="http://a/">http://a/</a>) ' +
'<a href="http://1.2/v:~-123">http://1.2/v:~-123</a>. c');
expect(errorLinkFilter(undefined)).not.toBeDefined();
});
it('should handle mailto', function () {
expect(errorLinkFilter("mailto:me@example.com")).
toBe('<a href="mailto:me@example.com">me@example.com</a>');
expect(errorLinkFilter("me@example.com")).
toBe('<a href="mailto:me@example.com">me@example.com</a>');
expect(errorLinkFilter("send email to me@example.com, but")).
toBe('send email to <a href="mailto:me@example.com">me@example.com</a>, but');
});
it('should handle target', function () {
expect(errorLinkFilter("http://example.com", "_blank")).
toBe('<a target="_blank" href="http://example.com">http://example.com</a>')
expect(errorLinkFilter("http://example.com", "someNamedIFrame")).
toBe('<a target="someNamedIFrame" href="http://example.com">http://example.com</a>')
});
it('should not linkify stack trace URLs', function () {
expect(errorLinkFilter("http://example.com/angular.min.js:42:1337")).
toBe("http://example.com/angular.min.js:42:1337");
});
it('should truncate linked URLs at 60 characters', function () {
expect(errorLinkFilter("http://errors.angularjs.org/very-long-version-string/$injector/nomod?p0=myApp")).
toBe('<a href="http://errors.angularjs.org/very-long-version-string/$injector/nomod?p0=myApp">' +
'http://errors.angularjs.org/very-long-version-string/$inj...</a>');
});
});

View file

@ -1,32 +0,0 @@
// Copy/pasted from src/Angular.js, so that we can disable specific tests on IE.
var msie = parseInt((/msie (\d+)/.exec(navigator.userAgent.toLowerCase()) || [])[1], 10);
var createMockWindow = function() {
var mockWindow = {};
var setTimeoutQueue = [];
mockWindow.location = window.location;
mockWindow.document = window.document;
mockWindow.getComputedStyle = angular.bind(window, window.getComputedStyle);
mockWindow.scrollTo = angular.bind(window, window.scrollTo);
mockWindow.navigator = window.navigator;
mockWindow.setTimeout = function(fn, delay) {
setTimeoutQueue.push({fn: fn, delay: delay});
};
mockWindow.setTimeout.queue = setTimeoutQueue;
mockWindow.setTimeout.expect = function(delay) {
if (setTimeoutQueue.length > 0) {
return {
process: function() {
var tick = setTimeoutQueue.shift();
expect(tick.delay).toEqual(delay);
tick.fn();
}
};
} else {
expect('SetTimoutQueue empty. Expecting delay of ').toEqual(delay);
}
};
return mockWindow;
};

View file

@ -1,50 +0,0 @@
describe('Docs Syntax', function() {
beforeEach(module('bootstrap'));
describe('syntax', function() {
var id, element, document;
beforeEach(inject(function($compile, $rootScope, $document) {
document = $document[0];
//create the HTML elements missing in IE8 for this directive
document.createElement('nav');
element = angular.element(
'<div>' +
'<pre syntax ' +
'syntax-github="gh-url" ' +
'syntax-plunkr="pl-url" ' +
'syntax-fiddle="jf-url">' +
'</pre>' +
'</div>'
);
$compile(element)($rootScope);
$rootScope.$digest();
element = element[0];
document.body.appendChild(element);
}));
it("should properly prepare a github link in the page", function() {
var github = element.querySelector('.syntax-github');
expect(github.innerHTML).toMatch(/View on Github/i);
expect(github.getAttribute('href')).toBe('gh-url');
});
it("should properly prepare a plunkr link in the page", function() {
var plunkr = element.querySelector('.syntax-plunkr');
expect(plunkr.innerHTML).toMatch(/View on Plunkr/i);
expect(plunkr.getAttribute('href')).toBe('pl-url');
});
it("should properly prepare a jsfiddle link in the page", function() {
var jsfiddle = element.querySelector('.syntax-jsfiddle');
expect(jsfiddle.innerHTML).toMatch(/View on JSFiddle/i);
expect(jsfiddle.getAttribute('href')).toBe('jf-url');
});
});
});

View file

@ -1,33 +0,0 @@
describe('DocsApp', function() {
// Do not run this suite on Internet Explorer.
if (msie < 10) return;
beforeEach(module('docsApp'));
describe('DocsVersionsCtrl', function() {
var $scope, ctrl, window, version = '9.8.7';
beforeEach(function() {
module(function($provide) {
$provide.value('$window', window = createMockWindow());
});
inject(function($controller, $rootScope) {
$scope = $rootScope.$new();
$scope.version = version;
ctrl = $controller('DocsVersionsCtrl',{
$scope : $scope,
$window : window
});
});
});
describe('changing the URL', function() {
it('should jump to the url provided', function() {
$scope.jumpToDocsVersion({ version: '1.0.1', url : 'page123'});
expect(window.location).toBe('page123');
});
});
});
});

View file

@ -1,392 +0,0 @@
'use strict';
var directive = {};
directive.dropdownToggle =
['$document', '$location', '$window',
function ($document, $location, $window) {
var openElement = null, close;
return {
restrict: 'C',
link: function(scope, element, attrs) {
scope.$watch(function dropdownTogglePathWatch(){return $location.path();}, function dropdownTogglePathWatchAction() {
close && close();
});
element.parent().on('click', function(event) {
close && close();
});
element.on('click', function(event) {
event.preventDefault();
event.stopPropagation();
var iWasOpen = false;
if (openElement) {
iWasOpen = openElement === element;
close();
}
if (!iWasOpen){
element.parent().addClass('open');
openElement = element;
close = function (event) {
event && event.preventDefault();
event && event.stopPropagation();
$document.off('click', close);
element.parent().removeClass('open');
close = null;
openElement = null;
}
$document.on('click', close);
}
});
}
};
}];
directive.syntax = function() {
return {
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">' +
'<span class="' + icon + '"></span> ' + text +
'</a>';
};
var html = '';
var types = {
'github' : {
text : 'View on Github',
key : 'syntaxGithub',
icon : 'icon-github'
},
'plunkr' : {
text : 'View on Plunkr',
key : 'syntaxPlunkr',
icon : 'icon-arrow-down'
},
'jsfiddle' : {
text : 'View on JSFiddle',
key : 'syntaxFiddle',
icon : 'icon-cloud'
}
};
for(var type in types) {
var data = types[type];
var link = attrs[data.key];
if(link) {
html += makeLink(type, data.text, link, data.icon);
}
};
var nav = document.createElement('nav');
nav.className = 'syntax-links';
nav.innerHTML = html;
var node = element[0];
var par = node.parentNode;
par.insertBefore(nav, node);
}
}
}
directive.tabbable = function() {
return {
restrict: 'C',
compile: function(element) {
var navTabs = angular.element('<ul class="nav nav-tabs"></ul>'),
tabContent = angular.element('<div class="tab-content"></div>');
tabContent.append(element.contents());
element.append(navTabs).append(tabContent);
},
controller: ['$scope', '$element', function($scope, $element) {
var navTabs = $element.contents().eq(0),
ngModel = $element.controller('ngModel') || {},
tabs = [],
selectedTab;
ngModel.$render = function() {
var $viewValue = this.$viewValue;
if (selectedTab ? (selectedTab.value != $viewValue) : $viewValue) {
if(selectedTab) {
selectedTab.paneElement.removeClass('active');
selectedTab.tabElement.removeClass('active');
selectedTab = null;
}
if($viewValue) {
for(var i = 0, ii = tabs.length; i < ii; i++) {
if ($viewValue == tabs[i].value) {
selectedTab = tabs[i];
break;
}
}
if (selectedTab) {
selectedTab.paneElement.addClass('active');
selectedTab.tabElement.addClass('active');
}
}
}
};
this.addPane = function(element, attr) {
var li = angular.element('<li><a href></a></li>'),
a = li.find('a'),
tab = {
paneElement: element,
paneAttrs: attr,
tabElement: li
};
tabs.push(tab);
attr.$observe('value', update)();
attr.$observe('title', function(){ update(); a.text(tab.title); })();
function update() {
tab.title = attr.title;
tab.value = attr.value || attr.title;
if (!ngModel.$setViewValue && (!ngModel.$viewValue || tab == selectedTab)) {
// we are not part of angular
ngModel.$viewValue = tab.value;
}
ngModel.$render();
}
navTabs.append(li);
li.on('click', function(event) {
event.preventDefault();
event.stopPropagation();
if (ngModel.$setViewValue) {
$scope.$apply(function() {
ngModel.$setViewValue(tab.value);
ngModel.$render();
});
} else {
// we are not part of angular
ngModel.$viewValue = tab.value;
ngModel.$render();
}
});
return function() {
tab.tabElement.remove();
for(var i = 0, ii = tabs.length; i < ii; i++ ) {
if (tab == tabs[i]) {
tabs.splice(i, 1);
}
}
};
}
}]
};
};
directive.table = function() {
return {
restrict: 'E',
link: function(scope, element, attrs) {
if (!attrs['class']) {
element.addClass('table table-bordered table-striped code-table');
}
}
};
};
var popoverElement = function() {
var object = {
init : function() {
this.element = angular.element(
'<div class="popover popover-incode top">' +
'<div class="arrow"></div>' +
'<div class="popover-inner">' +
'<div class="popover-title"><code></code></div>' +
'<div class="popover-content"></div>' +
'</div>' +
'</div>'
);
this.node = this.element[0];
this.element.css({
'display':'block',
'position':'absolute'
});
angular.element(document.body).append(this.element);
var inner = this.element.children()[1];
this.titleElement = angular.element(inner.childNodes[0].firstChild);
this.contentElement = angular.element(inner.childNodes[1]);
//stop the click on the tooltip
this.element.bind('click', function(event) {
event.preventDefault();
event.stopPropagation();
});
var self = this;
angular.element(document.body).bind('click',function(event) {
if(self.visible()) self.hide();
});
},
show : function(x,y) {
this.element.addClass('visible');
this.position(x || 0, y || 0);
},
hide : function() {
this.element.removeClass('visible');
this.position(-9999,-9999);
},
visible : function() {
return this.position().y >= 0;
},
isSituatedAt : function(element) {
return this.besideElement ? element[0] == this.besideElement[0] : false;
},
title : function(value) {
return this.titleElement.html(value);
},
content : function(value) {
if(value && value.length > 0) {
value = marked(value);
}
return this.contentElement.html(value);
},
positionArrow : function(position) {
this.node.className = 'popover ' + position;
},
positionAway : function() {
this.besideElement = null;
this.hide();
},
positionBeside : function(element) {
this.besideElement = element;
var elm = element[0];
var x = elm.offsetLeft;
var y = elm.offsetTop;
x -= 30;
y -= this.node.offsetHeight + 10;
this.show(x,y);
},
position : function(x,y) {
if(x != null && y != null) {
this.element.css('left',x + 'px');
this.element.css('top', y + 'px');
}
else {
return {
x : this.node.offsetLeft,
y : this.node.offsetTop
};
}
}
};
object.init();
object.hide();
return object;
};
directive.popover = ['popoverElement', function(popover) {
return {
restrict: 'A',
priority : 500,
link: function(scope, element, attrs) {
element.bind('click',function(event) {
event.preventDefault();
event.stopPropagation();
if(popover.isSituatedAt(element) && popover.visible()) {
popover.title('');
popover.content('');
popover.positionAway();
}
else {
popover.title(attrs.title);
popover.content(attrs.content);
popover.positionBeside(element);
}
});
}
}
}];
directive.tabPane = function() {
return {
require: '^tabbable',
restrict: 'C',
link: function(scope, element, attrs, tabsCtrl) {
element.on('$remove', tabsCtrl.addPane(element, attrs));
}
};
};
directive.foldout = ['$http', '$animate','$window', function($http, $animate, $window) {
return {
restrict: 'A',
priority : 500,
link: function(scope, element, attrs) {
var container, loading, url = attrs.url;
if(/\/build\//.test($window.location.href)) {
url = '/build/docs' + url;
}
element.bind('click',function() {
scope.$apply(function() {
if(!container) {
if(loading) return;
loading = true;
var par = element.parent();
container = angular.element('<div class="foldout">loading...</div>');
$animate.enter(container, null, par);
$http.get(url, { cache : true }).success(function(html) {
loading = false;
html = '<div class="foldout-inner">' +
'<div calss="foldout-arrow"></div>' +
html +
'</div>';
container.html(html);
//avoid showing the element if the user has already closed it
if(container.css('display') == 'block') {
container.css('display','none');
$animate.addClass(container, 'ng-hide');
}
});
}
else {
container.hasClass('ng-hide') ? $animate.removeClass(container, 'ng-hide') : $animate.addClass(container, 'ng-hide');
}
});
});
}
}
}];
angular.module('bootstrap', [])
.directive(directive)
.factory('popoverElement', popoverElement)
.run(function() {
marked.setOptions({
gfm: true,
tables: true
});
});

View file

@ -2,297 +2,6 @@
@name API Reference
@description
# AngularJS API Docs
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/dev_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 `$$`**
To prevent accidental name collisions with your code,
Angular prefixes names of public objects with `$` and names of private objects with `$$`.
Please do not use the `$` or `$$` prefix in your code.
</div>
## Angular Namespace
## {@link ng ng (core module)}
This module is provided by default and contains the core components of AngularJS.
<table class="definition-table spaced">
<tr>
<td>{@link ng#directive Directives}</td>
<td>
<p>
This is the core collection of directives you would use in your template code to build an AngularJS application.
</p>
<p>
Some examples include:
{@link ng.directive:ngClick ngClick},
{@link ng.directive:ngInclude ngInclude},
{@link ng.directive:ngRepeat ngRepeat},
etc… <br />
</p>
</td>
</tr>
<tr>
<td>
{@link ng#service Services / Factories}
</td>
<td>
<p>
This is the core collection of services which are used within the DI of your application.
</p>
<p>
Some examples include:
{@link ng.$compile $compile},
{@link ng.$http $http},
{@link ngRoute.$routeParams $routeParams},
{@link ng.$location $location},
etc…
<p>
</td>
</tr>
<tr>
<td>
{@link ng#filter Filters}
</td>
<td>
<p>
The core filters available in the ng module are used to transform template data before it is rendered within directives and expressions.
</p>
<p>
Some examples include:
{@link ng.filter:filter filter},
{@link ng.filter:date date},
{@link ng.filter:currency currency},
{@link ng.filter:lowercase lowercase},
{@link ng.filter:uppercase uppercase},
etc...
</p>
</td>
</tr>
<tr>
<td>
{@link ng#function Global APIs}
</td>
<td>
<p>
The core global API functions are attached to the angular object. These core functions are useful for low level JavaScript operations within your application.
</p>
<p>
Some examples include:
{@link angular.copy angular.copy()},
{@link angular.equals angular.equals()},
{@link angular.element angular.element()},
etc...
</p>
</td>
</tr>
</table>
## {@link ngRoute ngRoute}
Use ngRoute to enable URL routing to your application. The ngRoute module supports URL management via both hashbang and HTML5 pushState.
<div class="alert alert-info">Include the **angular-route.js** file and set **ngRoute** as a dependency for this to work in your application.</div>
<table class="definition-table spaced">
<tr>
<td>
{@link ngRoute#service Services / Factories}
</td>
<td>
The following services are used for route management:
<ul>
<li>{@link ngRoute.$routeParams $routeParams} is used to access the querystring values present in the URL.</li>
<li>{@link ngRoute.$route $route} is used to access the details of the route that is currently being accessed.</li>
<li>{@link ngRoute.$routeProvider $routeProvider} is used to register routes for the application.</li>
</ul>
</td>
</tr>
<tr>
<td>
{@link ngRoute#directive Directives}
</td>
<td>
The {@link ngRoute.directive:ngView ngView} directive will display the template of the current route within the page.
</td>
</tr>
</table>
## {@link ngAnimate ngAnimate}
Use ngAnimate to enable animation features into your application. Various core ng directives will provide
animation hooks into your application when ngAnimate is included. Animations are defined by using CSS transitions/animations
or JavaScript callbacks.
<div class="alert alert-info">Include the **angular-animate.js** file and set **ngAnimate** as a dependency for this to work in your application.</div>
<table class="definition-table spaced">
<tr>
<td>
{@link ngAnimate#service Services / Factories}
</td>
<td>
Use {@link ngAnimate.$animate $animate} to trigger animation operations within your directive code.
</td>
</tr>
<tr>
<td>
{@link ngAnimate CSS-based animations}
</td>
<td>
Follow ngAnimates CSS naming structure to reference CSS transitions / keyframe animations in AngularJS. Once defined the animation can be triggered by referencing the CSS class within the HTML template code.
</td>
</tr>
<tr>
<td>
{@link ngAnimate JS-based animations}
</td>
<td>
Use {@link angular.Module#methods_animation module.animation()} to register a JavaScript animation. Once registered the animation can be triggered by referencing the CSS class within the HTML template code.
</td>
</tr>
</table>
## {@link ngResource ngResource}
Use the ngResource module when querying and posting data to a REST API.
<div class="alert alert-info">Include the **angular-resource.js** file and set **ngResource** as a dependency for this to work in your application.</div>
<table class="definition-table spaced">
<tr>
<td>
{@link ngResource#service Services / Factories}
</td>
<td>
The {@link ngResource.$resource $resource} service is used to define RESTful objects which communicate with a REST API.
</td>
</tr>
</table>
## {@link ngCookies ngCookies}
Use the ngCookies module to handle cookie management within your application.
<div class="alert alert-info">Include the **angular-cookies.js** file and set **ngCookies** as a dependency for this to work in your application.</div>
<table class="definition-table spaced">
<tr>
<td>
{@link ngCookies#service Services / Factories}
</td>
<td>
The following services are used for cookie management:
<ul>
<li>The {@link ngCookies.$cookies $cookie} service is a convenient wrapper to store simple data within browser cookies.</li>
<li>{@link ngCookies.$cookieStore $cookieStore} is used to store more complex data using serialization.</li>
</ul>
</td>
</tr>
</table>
## {@link ngTouch ngTouch}
Use ngTouch when developing for mobile browsers/devices.
<div class="alert alert-info">Include the **angular-touch.js** file and set **ngTouch** as a dependency for this to work in your application.</div>
<table class="definition-table spaced">
<tr>
<td>
{@link ngTouch#service Services / Factories}
</td>
<td>
The {@link ngTouch.$swipe $swipe} service is used to register and manage mobile DOM events.
</td>
</tr>
<tr>
<td>
{@link ngTouch#directive Directives}
</td>
<td>
Various directives are available in ngTouch to emulate mobile DOM events.
</td>
</tr>
</table>
## {@link ngSanitize ngSanitize}
Use ngSanitize to securely parse and manipulate HTML data in your application.
<div class="alert alert-info">Include the **angular-sanitize.js** file and set **ngSanitize** as a dependency for this to work in your application.</div>
<table class="definition-table spaced">
<tr>
<td>
{@link ngSanitize#service Services / Factories}
</td>
<td>
The {@link ngSanitize.$sanitize $sanitize} service is used to clean up dangerous HTML code in a quick and convenient way.
</td>
</tr>
<tr>
<td>
{@link ngTouch#filter Filters}
</td>
<td>
The {@link ngSanitize.filter:linky linky filter} is used to turn URLs into HTML links within the provided string.
</td>
</tr>
</table>
## {@link ngMock ngMock}
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>
<table class="definition-table spaced">
<tr>
<td>
{@link ngMock#service Services / Factories}
</td>
<td>
<p>
ngMock will extend the behavior of various core services to become testing aware and manageable in a synchronous manner.
<p>
<p>
Some examples include:
{@link ngMock.$timeout $timeout},
{@link ngMock.$interval $interval},
{@link ngMock.$log $log},
{@link ngMock.$httpBackend $httpBackend},
etc...
<p>
</td>
</tr>
<tr>
<td>
{@link ngMock#function Global APIs}
</td>
<td>
<p>
Various helper functions are available to inject and mock modules within unit test code.
</p>
<p>
Some examples
{@link angular.mock.inject inject()},
{@link angular.mock.module module()},
{@link angular.mock.dump dump()},
etc...
<p>
</td>
</tr>
</table>
Use the API Reference documentation when you need more information about a specific feature. Check out
{@link guide/ Developer Guide} for AngularJS concepts. If you are new to AngularJS we recommend the
{@link tutorial/ Tutorial}.

View file

@ -2,10 +2,4 @@
@name ng
@description
# ng (core module)
The ng module is loaded by default when an AngularJS application is started. The module itself
contains the essential components for an AngularJS application to function. The table below
lists a high level breakdown of each of the services/factories, filters, directives and testing
components available within this core module.
<div doc-module-components="ng"></div>
The `ng` is an angular module which contains all of the core angular services.

View file

@ -0,0 +1,127 @@
@ngdoc overview
@name Cookbook: Advanced Form
@description
Here we extend the basic form example to include common features such as reverting, dirty state
detection, and preventing invalid form submission.
<doc:example>
<doc:source>
<script>
function UserForm($scope) {
var master = {
name: 'John Smith',
address:{
line1: '123 Main St.',
city:'Anytown',
state:'AA',
zip:'12345'
},
contacts:[
{type:'phone', value:'1(234) 555-1212'}
]
};
$scope.state = /^\w\w$/;
$scope.zip = /^\d\d\d\d\d$/;
$scope.cancel = function() {
$scope.form = angular.copy(master);
};
$scope.save = function() {
master = $scope.form;
$scope.cancel();
};
$scope.addContact = function() {
$scope.form.contacts.push({type:'', value:''});
};
$scope.removeContact = function(contact) {
var contacts = $scope.form.contacts;
for (var i = 0, ii = contacts.length; i < ii; i++) {
if (contact === contacts[i]) {
contacts.splice(i, 1);
}
}
};
$scope.isCancelDisabled = function() {
return angular.equals(master, $scope.form);
};
$scope.isSaveDisabled = function() {
return $scope.myForm.$invalid || angular.equals(master, $scope.form);
};
$scope.cancel();
}
</script>
<div ng-controller="UserForm">
<form name="myForm">
<label>Name:</label><br/>
<input type="text" ng-model="form.name" required/> <br/><br/>
<label>Address:</label> <br/>
<input type="text" ng-model="form.address.line1" size="33" required/> <br/>
<input type="text" ng-model="form.address.city" size="12" required/>,
<input type="text" ng-model="form.address.state" size="2"
ng-pattern="state" required/>
<input type="text" ng-model="form.address.zip" size="5"
ng-pattern="zip" required/><br/><br/>
<label>Contacts:</label>
[ <a href="" ng-click="addContact()">add</a> ]
<div ng-repeat="contact in form.contacts">
<select ng-model="contact.type">
<option>email</option>
<option>phone</option>
<option>pager</option>
<option>IM</option>
</select>
<input type="text" ng-model="contact.value" required/>
[ <a href="" ng-click="removeContact(contact)">X</a> ]
</div>
<button ng-click="cancel()" ng-disabled="isCancelDisabled()">Cancel</button>
<button ng-click="save()" ng-disabled="isSaveDisabled()">Save</button>
</form>
<hr/>
Debug View:
<pre>form={{form}}</pre>
</div>
</doc:source>
<doc:scenario>
it('should enable save button', function() {
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
input('form.name').enter('');
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
input('form.name').enter('change');
expect(element(':button:contains(Save)').attr('disabled')).toBeFalsy();
element(':button:contains(Save)').click();
expect(element(':button:contains(Save)').attr('disabled')).toBeTruthy();
});
it('should enable cancel button', function() {
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
input('form.name').enter('change');
expect(element(':button:contains(Cancel)').attr('disabled')).toBeFalsy();
element(':button:contains(Cancel)').click();
expect(element(':button:contains(Cancel)').attr('disabled')).toBeTruthy();
expect(element(':input[ng\\:model="form.name"]').val()).toEqual('John Smith');
});
</doc:scenario>
</doc:example>
#Things to notice
* Cancel & save buttons are only enabled if the form is dirty — there is something to cancel or
save.
* Save button is only enabled if there are no validation errors on the form.
* Cancel reverts the form changes back to original state.
* Save updates the internal model of the form.
* Debug view shows the two models. One presented to the user form and the other being the pristine
copy master.

View file

@ -0,0 +1,63 @@
@ngdoc overview
@name Cookbook: Resources - Buzz
@description
External resources are URLs that provide JSON data, which are then rendered with the help of
templates. Angular has a resource factory that can be used to give names to the URLs and then
attach behavior to them. For example you can use the
{@link http://code.google.com/apis/buzz/v1/getting_started.html#background-operations| Google Buzz
API}
to retrieve Buzz activity and comments.
<doc:example>
<doc:source>
<script>
BuzzController.$inject = ['$scope', '$resource'];
function BuzzController($scope, $resource) {
$scope.userId = 'googlebuzz';
$scope.Activity = $resource(
'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments',
{alt: 'json', callback: 'JSON_CALLBACK'},
{ get: {method: 'JSONP', params: {visibility: '@self'}},
replies: {method: 'JSONP', params: {visibility: '@self', comments: '@comments'}}
});
$scope.fetch = function() {
$scope.activities = $scope.Activity.get({userId:this.userId});
}
$scope.expandReplies = function(activity) {
activity.replies = $scope.Activity.replies({userId: this.userId, activityId: activity.id});
}
};
</script>
<div ng-controller="BuzzController">
<input ng-model="userId"/>
<button ng-click="fetch()">fetch</button>
<hr/>
<div class="buzz" ng-repeat="item in activities.data.items">
<h1 style="font-size: 15px;">
<img ng-src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
<a ng-href="{{item.actor.profileUrl}}">{{item.actor.name}}</a>
<a href ng-click="expandReplies(item)" style="float: right;">
Expand replies: {{item.links.replies[0].count}}
</a>
</h1>
{{item.object.content | html}}
<div class="reply" ng-repeat="reply in item.replies.data.items" style="margin-left: 20px;">
<img ng-src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/>
<a ng-href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>:
{{reply.content | html}}
</div>
</div>
</div>
</doc:source>
<doc:scenario>
xit('fetch buzz and expand', function() {
element(':button:contains(fetch)').click();
expect(repeater('div.buzz').count()).toBeGreaterThan(0);
element('.buzz a:contains(Expand replies):first').click();
expect(repeater('div.reply').count()).toBeGreaterThan(0);
});
</doc:scenario>
</doc:example>

View file

@ -0,0 +1,151 @@
@ngdoc overview
@name Cookbook: Deep Linking
@description
Deep linking allows you to encode the state of the application in the URL so that it can be
bookmarked and the application can be restored from the URL to the same state.
While Angular does not force you to deal with bookmarks in any particular way, it has services
which make the common case described here very easy to implement.
# Assumptions
Your application consists of a single HTML page which bootstraps the application. We will refer
to this page as the chrome.
Your application is divided into several screens (or views) which the user can visit. For example,
the home screen, settings screen, details screen, etc. For each of these screens, we would like to
assign a URL so that it can be bookmarked and later restored. Each of these screens will be
associated with a controller which define the screen's behavior. The most common case is that the
screen will be constructed from an HTML snippet, which we will refer to as the partial. Screens can
have multiple partials, but a single partial is the most common construct. This example makes the
partial boundary visible using a blue line.
You can make a routing table which shows which URL maps to which partial view template and which
controller.
# Example
In this example we have a simple app which consist of two screens:
* Welcome: url `welcome` Show the user contact information.
* Settings: url `settings` Show an edit screen for user contact information.
<example module="deepLinking" deps="angular-sanitize.js">
<file name="script.js">
angular.module('deepLinking', ['ngSanitize'])
.config(function($routeProvider) {
$routeProvider.
when("/welcome", {templateUrl:'welcome.html', controller:WelcomeCntl}).
when("/settings", {templateUrl:'settings.html', controller:SettingsCntl});
});
AppCntl.$inject = ['$scope', '$route']
function AppCntl($scope, $route) {
$scope.$route = $route;
// initialize the model to something useful
$scope.person = {
name:'anonymous',
contacts:[{type:'email', url:'anonymous@example.com'}]
};
}
function WelcomeCntl($scope) {
$scope.greet = function() {
alert("Hello " + $scope.person.name);
};
}
function SettingsCntl($scope, $location) {
$scope.cancel = function() {
$scope.form = angular.copy($scope.person);
};
$scope.save = function() {
angular.copy($scope.form, $scope.person);
$location.path('/welcome');
};
$scope.cancel();
}
</file>
<file name="style.css">
[ng-view] {
border: 1px solid blue;
margin: 0;
padding:1em;
}
.partial-info {
background-color: blue;
color: white;
padding: 3px;
}
</file>
<file name="index.html">
<div ng-controller="AppCntl">
<h1>Your App Chrome</h1>
[ <a href="welcome">Welcome</a> | <a href="settings">Settings</a> ]
<hr/>
<span class="partial-info">
Partial: {{$route.current.template}}
</span>
<div ng-view></div>
<small>Your app footer </small>
</div>
</file>
<file name="settings.html">
<label>Name:</label>
<input type="text" ng:model="form.name" required>
<div ng:repeat="contact in form.contacts">
<select ng:model="contact.type">
<option>url</option>
<option>email</option>
<option>phone</option>
</select>
<input type="text" ng:model="contact.url">
[ <a href="" ng:click="form.contacts.$remove(contact)">X</a> ]
</div>
<div>
[ <a href="" ng:click="form.contacts.$add()">add</a> ]
</div>
<button ng:click="cancel()">Cancel</button>
<button ng:click="save()">Save</button>
</file>
<file name="welcome.html">
Hello {{person.name}},
<div>
Your contact information:
<div ng:repeat="contact in person.contacts">{{contact.type}}:
<span ng-bind-html="contact.url|linky"></span>
</div>
</div>
</file>
<file name="scenario.js">
it('should navigate to URL', function() {
element('a:contains(Welcome)').click();
expect(element('[ng-view]').text()).toMatch(/Hello anonymous/);
element('a:contains(Settings)').click();
input('form.name').enter('yourname');
element(':button:contains(Save)').click();
element('a:contains(Welcome)').click();
expect(element('[ng-view]').text()).toMatch(/Hello yourname/);
});
</file>
</example>
# Things to notice
* Routes are defined in the `AppCntl` class. The initialization of the controller causes the
initialization of the {@link api/ng.$route $route} service with the proper URL
routes.
* The {@link api/ng.$route $route} service then watches the URL and instantiates the
appropriate controller when the URL changes.
* The {@link api/ng.directive:ngView ngView} widget loads the
view when the URL changes. It also sets the view scope to the newly instantiated controller.
* Changing the URL is sufficient to change the controller and view. It makes no difference whether
the URL is changed programatically or by the user.

View file

@ -0,0 +1,114 @@
@ngdoc overview
@name Cookbook: Form
@description
A web application's main purpose is to present and gather data. For this reason Angular strives
to make both of these operations trivial. This example shows off how you can build a simple form to
allow a user to enter data.
<doc:example>
<doc:source>
<script>
function FormController($scope) {
var user = $scope.user = {
name: 'John Smith',
address:{line1: '123 Main St.', city:'Anytown', state:'AA', zip:'12345'},
contacts:[{type:'phone', value:'1(234) 555-1212'}]
};
$scope.state = /^\w\w$/;
$scope.zip = /^\d\d\d\d\d$/;
$scope.addContact = function() {
user.contacts.push({type:'email', value:''});
};
$scope.removeContact = function(contact) {
for (var i = 0, ii = user.contacts.length; i < ii; i++) {
if (contact === user.contacts[i]) {
$scope.user.contacts.splice(i, 1);
}
}
};
}
</script>
<div ng-controller="FormController" class="example">
<label>Name:</label><br>
<input type="text" ng-model="user.name" required/> <br><br>
<label>Address:</label><br>
<input type="text" ng-model="user.address.line1" size="33" required> <br>
<input type="text" ng-model="user.address.city" size="12" required>,
<input type="text" ng-model="user.address.state"
ng-pattern="state" size="2" required>
<input type="text" ng-model="user.address.zip" size="5"
ng-pattern="zip" required><br><br>
<label>Phone:</label>
[ <a href="" ng-click="addContact()">add</a> ]
<div ng-repeat="contact in user.contacts">
<select ng-model="contact.type">
<option>email</option>
<option>phone</option>
<option>pager</option>
<option>IM</option>
</select>
<input type="text" ng-model="contact.value" required>
[ <a href="" ng-click="removeContact(contact)">X</a> ]
</div>
<hr/>
Debug View:
<pre>user={{user | json}}</pre>
</div>
</doc:source>
<doc:scenario>
it('should show debug', function() {
expect(binding('user')).toMatch(/John Smith/);
});
it('should add contact', function() {
using('.example').element('a:contains(add)').click();
using('.example div:last').input('contact.value').enter('you@example.org');
expect(binding('user')).toMatch(/\(234\) 555\-1212/);
expect(binding('user')).toMatch(/you@example.org/);
});
it('should remove contact', function() {
using('.example').element('a:contains(X)').click();
expect(binding('user')).not().toMatch(/\(234\) 555\-1212/);
});
it('should validate zip', function() {
expect(using('.example').
element(':input[ng\\:model="user.address.zip"]').
prop('className')).not().toMatch(/ng-invalid/);
using('.example').input('user.address.zip').enter('abc');
expect(using('.example').
element(':input[ng\\:model="user.address.zip"]').
prop('className')).toMatch(/ng-invalid/);
});
it('should validate state', function() {
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
.not().toMatch(/ng-invalid/);
using('.example').input('user.address.state').enter('XXX');
expect(using('.example').element(':input[ng\\:model="user.address.state"]').prop('className'))
.toMatch(/ng-invalid/);
});
</doc:scenario>
</doc:example>
# Things to notice
* The user data model is initialized {@link api/ng.directive:ngController controller} and is
available in the {@link api/ng.$rootScope.Scope scope} with the initial data.
* For debugging purposes we have included a debug view of the model to better understand what
is going on.
* The {@link api/ng.directive:input input directives} simply refer
to the model and are data-bound.
* The inputs validate. (Try leaving them blank or entering non digits in the zip field)
* In your application you can simply read from or write to the model and the form will be updated.
* By clicking the 'add' link you are adding new items into the `user.contacts` array which are then
reflected in the view.

View file

@ -0,0 +1,39 @@
@ngdoc overview
@name Cookbook: Hello World
@description
<doc:example>
<doc:source>
<script>
function HelloCntl($scope) {
$scope.name = 'World';
}
</script>
<div ng-controller="HelloCntl">
Your name: <input type="text" ng-model="name" value="World"/>
<hr/>
Hello {{name}}!
</div>
</doc:source>
<doc:scenario>
it('should change the binding when user enters text', function() {
expect(binding('name')).toEqual('World');
input('name').enter('angular');
expect(binding('name')).toEqual('angular');
});
</doc:scenario>
</doc:example>
# Things to notice
Take a look through the source and note:
* The script tag that {@link guide/bootstrap bootstraps} the Angular environment.
* The text {@link api/ng.directive:input input form control} which is
bound to the greeting name text.
* There is no need for listener registration and event firing on change events.
* The implicit presence of the `name` variable which is in the root {@link api/ng.$rootScope.Scope scope}.
* The double curly brace `{{markup}}`, which binds the name variable to the greeting text.
* The concept of {@link guide/dev_guide.templates.databinding data binding}, which reflects any
changes to the
input field in the greeting text.

View file

@ -0,0 +1,58 @@
@ngdoc overview
@name Cookbook
@description
Welcome to the Angular cookbook. Here we will show you typical uses of Angular by example.
# Hello World
{@link helloworld Hello World}: The simplest possible application that demonstrates the
classic Hello World!
# Basic Form
{@link form Basic Form}: Displaying forms to the user for editing is the bread and butter
of web applications. Angular makes forms easy through bidirectional data binding.
# Advanced Form
{@link advancedform Advanced Form}: Taking the form example to the next level and
providing advanced features such as dirty detection, form reverting and submit disabling if
validation errors exist.
# Model View Controller
{@link mvc MVC}: Tic-Tac-Toe: Model View Controller (MVC) is a time-tested design pattern
to separate the behavior (JavaScript controller) from the presentation (HTML view). This
separation aids in maintainability and testability of your project.
# Multi-page App and Deep Linking
{@link deeplinking Deep Linking}: An AJAX application never navigates away from the
first page it loads. Instead, it changes the DOM of its single page. Eliminating full-page reloads
is what makes AJAX apps responsive, but it creates a problem in that apps with a single URL
prevent you from emailing links to a particular screen within your application.
Deep linking tries to solve this by changing the URL anchor without reloading a page, thus
allowing you to send links to specific screens in your app.
# Services
{@link api/ng Services}: Services are long lived objects in your applications that are
available across controllers. A collection of useful services are pre-bundled with Angular but you
will likely add your own. Services are initialized using dependency injection, which resolves the
order of initialization. This safeguards you from the perils of global state (a common way to
implement long lived objects).
# External Resources
{@link buzz Resources}: Web applications must be able to communicate with the external
services to get and update data. Resources are the abstractions of external URLs which are
specially tailored to Angular data binding.

View file

@ -0,0 +1,128 @@
@ngdoc overview
@name Cookbook: MVC
@description
MVC allows for a clean and testable separation between the behavior (controller) and the view
(HTML template). A Controller is just a JavaScript class which is grafted onto the scope of the
view. This makes it very easy for the controller and the view to share the model.
The model is a set of objects and primitives that are referenced from the Scope ($scope) object.
This makes it very easy to test the controller in isolation since one can simply instantiate the
controller and test without a view, because there is no connection between the controller and the
view.
<doc:example>
<doc:source>
<script>
function TicTacToeCntl($scope, $location) {
$scope.cellStyle= {
'height': '20px',
'width': '20px',
'border': '1px solid black',
'text-align': 'center',
'vertical-align': 'middle',
'cursor': 'pointer'
};
$scope.reset = function() {
$scope.board = [
['', '', ''],
['', '', ''],
['', '', '']
];
$scope.nextMove = 'X';
$scope.winner = '';
setUrl();
};
$scope.dropPiece = function(row, col) {
if (!$scope.winner && !$scope.board[row][col]) {
$scope.board[row][col] = $scope.nextMove;
$scope.nextMove = $scope.nextMove == 'X' ? 'O' : 'X';
setUrl();
}
};
$scope.reset();
$scope.$watch(function() { return $location.search().board;}, readUrl);
function setUrl() {
var rows = [];
angular.forEach($scope.board, function(row) {
rows.push(row.join(','));
});
$location.search({board: rows.join(';') + '/' + $scope.nextMove});
}
function grade() {
var b = $scope.board;
$scope.winner =
row(0) || row(1) || row(2) ||
col(0) || col(1) || col(2) ||
diagonal(-1) || diagonal(1);
function row(row) { return same(b[row][0], b[row][1], b[row][2]);}
function col(col) { return same(b[0][col], b[1][col], b[2][col]);}
function diagonal(i) { return same(b[0][1-i], b[1][1], b[2][1+i]);}
function same(a, b, c) { return (a==b && b==c) ? a : '';};
}
function readUrl(value) {
if (value) {
value = value.split('/');
$scope.nextMove = value[1];
angular.forEach(value[0].split(';'), function(row, col){
$scope.board[col] = row.split(',');
});
grade();
}
}
}
</script>
<h3>Tic-Tac-Toe</h3>
<div ng-controller="TicTacToeCntl">
Next Player: {{nextMove}}
<div class="winner" ng-show="winner">Player {{winner}} has won!</div>
<table class="board">
<tr ng-repeat="row in board" style="height:15px;">
<td ng-repeat="cell in row" ng-style="cellStyle"
ng-click="dropPiece($parent.$index, $index)">{{cell}}</td>
</tr>
</table>
<button ng-click="reset()">reset board</button>
</div>
</doc:source>
<doc:scenario>
it('should play a game', function() {
piece(1, 1);
expect(binding('nextMove')).toEqual('O');
piece(3, 1);
expect(binding('nextMove')).toEqual('X');
piece(1, 2);
piece(3, 2);
piece(1, 3);
expect(element('.winner').text()).toEqual('Player X has won!');
});
function piece(row, col) {
element('.board tr:nth-child('+row+') td:nth-child('+col+')').click();
}
</doc:scenario>
</doc:example>
# Things to notice
* The controller is defined in JavaScript and has no reference to the rendering logic.
* The controller is instantiated by Angular and injected into the view.
* The controller can be instantiated in isolation (without a view) and the code will still execute.
This makes it very testable.
* The HTML view is a projection of the model. In the above example, the model is stored in the
board variable.
* All of the controller's properties (such as board and nextMove) are available to the view.
* Changing the model changes the view.
* The view can call any controller function.
* In this example, the `setUrl()` and `readUrl()` functions copy the game state to/from the URL's
hash so the browser's back button will undo game steps. See deep-linking. This example calls {@link
api/ng.$rootScope.Scope#$watch $watch()} to set up a listener that invokes `readUrl()` when needed.

View file

@ -1,6 +0,0 @@
@ngdoc error
@name $animate:notcsel
@fullName Not class CSS selector
@description
Expecting a CSS selector for class. Class selectors must start with `.`, for example: `.my-class-name`.

View file

@ -1,8 +0,0 @@
@ngdoc error
@name $cacheFactory:iid
@fullName Invalid ID
@description
This error occurs when trying to create a new `cache` object via {@link api/ng.$cacheFactory} with an ID that was already used to create another cache object.
To resolve the error please use a different cache ID when calling `$cacheFactory`.

View file

@ -1,49 +0,0 @@
@ngdoc error
@name $compile:ctreq
@fullName Missing Required Controller
@description
This error occurs when {@link api/ng.$compile HTML compiler} tries to process a directive that specifies the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object `require` option} in a {@link api/ng.$compile#description_comprehensive-directive-api directive definition},
but the required directive controller is not present on the current DOM element (or its ancestor element, if `^` was specified).
To resolve this error ensure that there is no typo in the required controller name and that the required directive controller is present on the current element.
If the required controller is expected to be on a ancestor element, make ensure that you prefix the controller name in the `require` definition with `^`.
If the required controller is optionally requested, use `?` or `^?` to specify that.
Example of a directive that requires {@link api/ng.directive:ngModel ngModel} controller:
```
myApp.directive('myDirective', function() {
return {
require: 'ngModel',
...
}
}
```
This directive can then be used as:
```
<input ng-model="some.path" my-directive>
```
Example of a directive that optionally requires a {@link api/ng.directive:form form} controller from an ancestor:
```
myApp.directive('myDirective', function() {
return {
require: '^?form',
...
}
}
```
This directive can then be used as:
```
<form name="myForm">
<div>
<span my-directive></span>
</div>
</form>
```

View file

@ -1,25 +0,0 @@
@ngdoc error
@name $compile:iscp
@fullName Invalid Isolate Scope Definition
@description
When declaring isolate scope the scope definition object must be in specific format which starts with mode character (`@&=`) with an optional local name.
```
myModule.directive('directiveName', function factory() {
return {
...
scope: {
'attrName': '@', // OK
'attrName2': '=localName', // OK
'attrName3': 'name', // ERROR: missing mode @&=
'attrName4': ' = name', // ERROR: extra spaces
'attrName5': 'name=', // ERROR: must be prefixed with @&=
}
...
}
});
```
Please refer to the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object
`scope` option} of the directive definition documentation to learn more about the API.

View file

@ -1,17 +0,0 @@
@ngdoc error
@name $compile:multidir
@fullName Multiple Directive Resource Contention
@description
This error occurs when multiple directives are applied to the same DOM element, and
processing them would result in a collision or an unsupported configuration.
To resolve this issue remove one of the directives which is causing the collision.
Example scenarios of multiple incompatible directives applied to the same element include:
* Multiple directives requesting `isolated scope`.
* Multiple directives publishing a controller under the same name.
* Multiple directives declared with the `transclusion` option.
* Multiple directives attempting to define a `template` or `templateURL`.

View file

@ -1,20 +0,0 @@
@ngdoc error
@name $compile:nodomevents
@fullName Interpolated Event Attributes
@description
This error occurs when one tries to create a binding for event handler attributes like `onclick`, `onload`, `onsubmit`, etc.
There is no practical value in binding to these attributes and doing so only exposes your application to security vulnerabilities like XSS.
For these reasons binding to event handler attributes (all attributes that start with `on` and `formaction` attribute) is not supported.
An example code that would allow XSS vulnerability by evaluating user input in the window context could look like this:
```
<input ng-model="username">
<div onclick="{{username}}">click me</div>
```
Since the `onclick` evaluates the value as JavaScript code in the window context, setting the `username` model to a value like `javascript:alert('PWND')` would result in script injection when the `div` is clicked.

View file

@ -1,41 +0,0 @@
@ngdoc error
@name $compile:nonassign
@fullName Non-Assignable Expression
@description
This error occurs when a directive defines an isolate scope property
(using the `=` mode in the {@link api/ng.$compile#description_comprehensive-directive-api_directive-definition-object
`scope` option} of a directive definition) but the directive is used with an expression that is not-assignable.
In order for the two-way data-binding to work, it must be possible to write new values back into the path defined with the expression.
For example, given a directive:
```
myModule.directive('myDirective', function factory() {
return {
...
scope: {
'bind': '=localValue'
}
...
}
});
```
Following are invalid uses of this directive:
```
<!-- ERROR because `1+2=localValue` is an invalid statement -->
<my-directive bind="1+2">
<!-- ERROR because `myFn()=localValue` is an invalid statement -->
<my-directive bind="myFn()">
```
To resolve this error, always use path expressions with scope properties that are two-way data-bound:
```
<my-directive bind="some.property">
<my-directive bind="some[3]['property']">
```

View file

@ -1,20 +0,0 @@
@ngdoc error
@name $compile:selmulti
@fullName Binding to Multiple Attribute
@description
Binding to the `multiple` attribute of `select` element is not supported since switching between multiple and single mode changes the {@link api/ng.directive:ngModel `ngModel`} object type from instance to array of instances which breaks the model semantics.
If you need to use different types of `select` elements in your template based on some variable, please use {@link api/ng.directive:ngIf ngIf} or {@link api/ng.directive:ngSwitch ngSwitch} directives to select one of them to be used at runtime.
Example with invalid usage:
```
<select ng-model="some.model" multiple="{{mode}}"></select>
```
Example that uses ngIf to pick one of the `select` elements based on a variable:
```
<select ng-if="mode == 'multiple'" ng-model="some.model" multiple></select>
<select ng-if="mode != 'multiple'" ng-model="some.model"></select>
```

View file

@ -1,11 +0,0 @@
@ngdoc error
@name $compile:tpload
@fullName Error Loading Template
@description
This error occurs when {@link api/ng.$compile `$compile`} attempts to fetch a template from some URL, and the request fails.
To resolve this error, ensure that the URL of the template is spelled correctly and resolves to correct absolute URL.
The [Chrome Developer Tools](https://developers.google.com/chrome-developer-tools/docs/network#network_panel_overview) might also be helpful in determining why the request failed.
If you are using {@link api/ng.$templateCache} to pre-load templates, ensure that the cache was populated with the template.

View file

@ -1,39 +0,0 @@
@ngdoc error
@name $compile:tplrt
@fullName Invalid Template Root
@description
When a directive is declared with `template` (or `templateUrl`) and `replace` mode on, the template
must have exactly one root element. That is, the text of the template property or the content
referenced by the templateUrl must be contained within a single html element.
For example, `<p>blah <em>blah</em> blah</p>` instead of simply `blah <em>blah</em> blah`.
Otherwise, the replacement operation would result in a single element (the directive) being replaced
with multiple elements or nodes, which is unsupported and not commonly needed in practice.
For example a directive with definition:
```
myModule.directive('myDirective', function factory() {
return {
...
replace: true,
templateUrl: 'someUrl'
...
}
});
```
And a template provided at URL `someUrl`. The template must be an html fragment that has only a
single root element, like the `div` element in this template:
```
<div><b>Hello</b> World!</div>
```
An an invalid template to be used with this directive is one that defines multiple root nodes or
elements. For example:
```
<b>Hello</b> World!
```

View file

@ -1,34 +0,0 @@
@ngdoc error
@name $compile:uterdir
@fullName Unterminated Directive
@description
This error occurs when using multi-element directives and a `directive-start` attribute fails to form a matching pair with a corresponding `directive-end` attribute.
A `directive-start` should have a matching `directive-end` on a sibling node in the DOM. For instance,
```
<table>
<tr ng-repeat-start="item in list">I get repeated</tr>
<tr ng-repeat-end>I also get repeated</tr>
</table>
```
is a valid example.
This error can occur in several different ways. One is by leaving out the `directive-end` attribute, like so:
```
<div>
<span foo-start></span>
</div>
```
Another is by nesting a `directive-end` inside of `directive-start`, or vice versa:
```
<div>
<span foo-start><span foo-end></span></span>
</div>
```
To avoid this error, make sure each `directive-start` you use has a matching `directive-end` on a sibling node in the DOM.

View file

@ -1,21 +0,0 @@
@ngdoc error
@name $controller:noscp
@fullName Missing $scope object
@description
This error occurs when {@link api/ng.$controller $controller} service is called in order to instantiate a new controller but no scope is provided via `$scope` property of the locals map.
Example of incorrect usage that leads to this error:
```
$controller(MyController);
//or
$controller(MyController, {scope: newScope});
```
To fix the example above please provide a scope to the $controller call:
```
$controller(MyController, {$scope, newScope});
```
Please consult the {@link api/ng.$controller $controller} service api docs to learn more.

View file

@ -1,10 +0,0 @@
@ngdoc error
@name $httpBackend:noxhr
@fullName Unsupported XHR
@description
This error occurs in browsers that do not support XmlHttpRequest. AngularJS
supports Safari, Chrome, Firefox, Opera, IE8 and higher, and mobile browsers
(Android, Chrome Mobile, iOS Safari). To avoid this error, use an officially
supported browser.

View file

@ -1,13 +0,0 @@
@ngdoc overview
@name Error Reference
@description
Use the Error Reference manual to find information about error conditions in
your AngularJS app. Errors thrown in production builds of AngularJS will log
links to this site on the console.
Other useful references for debugging your app include:
- {@link api/ API Reference} for detailed information about specific features
- {@link guide/ Developer Guide} for AngularJS concepts
- {@link tutorial/ Tutorial} for getting started

View file

@ -1,26 +0,0 @@
@ngdoc error
@name $injector:cdep
@fullName Circular Dependency
@description
This error occurs when the {@link api/angular.injector $injector} tries to get
a service that depends on itself, either directly or indirectly. To fix this,
construct your dependency chain such that there are no circular dependencies.
For example:
```
angular.module('myApp', [])
.factory('myService', function (myService) {
// ...
})
.controller('MyCtrl', function ($scope, myService) {
// ...
});
```
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}.

View file

@ -1,26 +0,0 @@
@ngdoc error
@name $injector:itkn
@fullName Bad Injection Token
@description
This error occurs when using a bad token as a dependency injection annotation.
Dependency injection annotation tokens should always be strings. Using any other
type will cause this error to be thrown.
Examples of code with bad injection tokens include:
```
var myCtrl = function ($scope, $http) { /* ... */ };
myCtrl.$inject = ['$scope', 42];
myAppModule.controller('MyCtrl', ['$scope', {}, function ($scope, $timeout) {
// ...
}]);
```
The bad injection tokens are `42` in the first example and `{}` in the second.
To avoid the error, always use string literals for dependency injection annotation
tokens.
For an explanation of what injection annotations are and how to use them, refer
to the {@link guide/di Dependency Injection Guide}.

View file

@ -1,11 +0,0 @@
@ngdoc error
@name $injector:modulerr
@fullName Module Error
@description
This error occurs when a module fails to load due to some exception. The error
message above should provide additional context.
In AngularJS `1.2.0` and later, `ngRoute` has been moved to its own module.
If you are getting this error after upgrading to `1.2.x`, be sure that you've
installed {@link api/ngRoute `ngRoute`}.

View file

@ -1,26 +0,0 @@
@ngdoc error
@name $injector:nomod
@fullName Module Unavailable
@description
This error occurs when trying to "re-open" a module that has not yet been defined.
To define a new module, call {@link api/angular.module angular.module} with a name
and an array of dependent modules, like so:
```
// When defining a module with no module dependencies,
// the requires array should be defined and empty.
var myApp = angular.module('myApp', []);
```
To retrieve a reference to the same module for further configuration, call
`angular.module` without the `requires` array.
```
var myApp = angular.module('myApp');
```
Calling `angular.module` without the `requires` array when the module has not yet
been defined causes this error to be thrown. To fix it, define your module with
a name and an empty array, as in the first example above.

View file

@ -1,26 +0,0 @@
@ngdoc error
@name $injector:pget
@fullName Provider Missing $get
@description
This error occurs when attempting to register a provider that does not have a
`$get` method. For example:
```
function BadProvider() {} // No $get method!
angular.module("myApp", [])
.provider('bad', BadProvider); // this throws the error
```
To fix the error, fill in the `$get` method on the provider like so:
```
function GoodProvider() {
this.$get = angular.noop;
}
angular.module("myApp", [])
.provider('good', GoodProvider);
```
For more information, refer to the {@link api/AUTO.$provide#methods_provider
$provide.provider} api doc.

View file

@ -1,26 +0,0 @@
@ngdoc error
@name $injector:unpr
@fullName Unknown Provider
@description
This error results from the `$injector` being unable to resolve a required
dependency. To fix this, make sure the dependency is defined and spelled
correctly. For example:
```
angular.module('myApp', [])
.controller('myCtrl', ['myService', function (myService) {
// Do something with myService
}]);
```
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', [])
.service('myService', function () { /* ... */ })
.controller('myCtrl', ['myService', function (myService) {
// Do something with myService
}]);
```

View file

@ -1,7 +0,0 @@
@ngdoc error
@name $interpolate:interr
@fullName Interpolation Error
@description
This error occurs when interpolation fails due to some exception. The error
message above should provide additional context.

View file

@ -1,12 +0,0 @@
@ngdoc error
@name $interpolate:noconcat
@fullName Multiple Expressions
@description
This error occurs when performing an interpolation that concatenates multiple
expressions when a trusted value is required. Concatenating expressions makes
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 api/ng.$sce $sce} API doc.

View file

@ -1,11 +0,0 @@
@ngdoc error
@name jqLite:nosel
@fullName Unsupported Selector Lookup
@description
In order to keep Angular small, Angular implements only a subset of the selectors in {@link api/angular.element#description_angulars-jqlite jqLite}.
This error occurs when a jqLite instance is invoked with a selector other than this subset.
In order to resolve this error, rewrite your code to only use tag name selectors and manually traverse the DOM using the APIs provided by jqLite.
Alternatively, you can include a full version of jQuery, which Angular will automatically use and that will make all selectors available.

View file

@ -1,7 +0,0 @@
@ngdoc error
@name jqLite:offargs
@fullName Invalid jqLite#off() parameter
@description
This error occurs when trying to pass too many arguments to `jqLite#off`. Note
that `jqLite#off` does not support namespaces or selectors like jQuery.

View file

@ -1,8 +0,0 @@
@ngdoc error
@name jqLite:onargs
@fullName Invalid jqLite#on() Parameters
@description
This error occurs when trying to pass too many arguments to `jqLite#on`. Note
that `jqLite#on` does not support the `selector` or `eventData` parameters as
jQuery does.

View file

@ -1,17 +0,0 @@
@ngdoc error
@name $location:ihshprfx
@fullName Missing Hash Prefix
@description
This error occurs when {@link api/ng.$location $location} service is configured to use a hash prefix but this prefix was not present in a url that the `$location` service was asked to parse.
For example if you configure `$location` service with prefix `'!'`:
```
myApp.config(function($locationProvider) {
$locationProvider.prefix('!');
});
```
If you enter the app at url `http:/myapp.com/#/myView` this error will be throw.
The correct url for this configuration is `http:/myapp.com/#!/myView` (note the `'!'` after `'#'` symbol).

View file

@ -1,8 +0,0 @@
@ngdoc error
@name $location:ipthprfx
@fullName Invalid or Missing Path Prefix
@description
This error occurs when you configure the {@link api/ng.$location `$location`} service in the html5 mode, specify a base url for your application via `<base>` element and try to update the location with a path that doesn't match the base prefix.
To resolve this issue, please check the base url specified via the `<base>` tag in the head of your main html document, as well as the url that you tried to set the location to.

View file

@ -1,9 +0,0 @@
@ngdoc error
@name $location:isrcharg
@fullName Wrong $location.search() argument type
@description
To resolve this error, ensure that the first argument for the `$location.search` call is a `string` or an object.
You can use the stack trace associated with this error to identify the call site that caused this issue.
To learn more, please consult the {@link api/ng.$location `$location`} api docs.

View file

@ -1,8 +0,0 @@
@ngdoc error
@name ng:areq
@fullName Bad Argument
@description
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.

View file

@ -1,8 +0,0 @@
@ngdoc error
@name ng:badname
@fullName Bad `hasOwnProperty` Name
@description
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.

View file

@ -1,29 +0,0 @@
@ngdoc error
@name ng:btstrpd
@fullName App Already Bootstrapped with this Element
@description
Occurs when calling angular.bootstrap on an element that has already been bootstrapped.
This usually happens when you accidentally use both `ng-app` and `angular.bootstrap` to bootstrap an application.
```
<html>
...
<body ng-app="myApp">
<script>
angular.bootstrap(document.body, ['myApp']);
</script>
</body>
</html>
```
Note that for bootrapping purposes, the `<html>` element is the same as `document`, so the following will also throw an error.
```
<html>
...
<script>
angular.bootstrap(document, ['myApp']);
</script>
</html>
```

View file

@ -1,10 +0,0 @@
@ngdoc error
@name ng:cpi
@fullName Bad Copy
@description
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.

View file

@ -1,10 +0,0 @@
@ngdoc error
@name ng:cpws
@fullName Copying Window or Scope
@description
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.

View file

@ -1,27 +0,0 @@
@ngdoc error
@name ngModel:nonassign
@fullName Non-Assignable Expression
@description
This error occurs when expression the {@link api/ng.directive:ngModel ngModel} directive is bound to is a non-assignable expression.
Examples using assignable expressions include:
```
<input ng-model="namedVariable">
<input ng-model="myObj.someProperty">
<input ng-model="indexedArray[0]">
```
Examples of non-assignable expressions include:
```
<input ng-model="foo + bar">
<input ng-model="42">
<input ng-model="'oops'">
<input ng-model="myFunc()">
```
Always make sure that the expression bound via `ngModel` directive can be assigned to.
For more information, see the {@link api/ng.directive:ngModel ngModel API doc}.

View file

@ -1,13 +0,0 @@
@ngdoc error
@name ngOptions:iexp
@fullName Invalid Expression
@description
This error occurs when 'ngOptions' is passed an expression that isn't in an expected form.
Here's an example of correct syntax:
```
<select ng-model="color" ng-options="c.name for c in colors">
```
For more information on valid expression syntax, see 'ngOptions' in {@link api/ng.directive:select select} directive docs.

View file

@ -1,7 +0,0 @@
@ngdoc error
@name ngPattern:noregexp
@fullName Expected Regular Expression
@description
This error occurs when 'ngPattern' is passed an expression that isn't a regular expression or doesn't have the expected format.
For more information on valid expression syntax, see 'ngPattern' in {@link api/ng.directive:input input} directive docs.

View file

@ -1,22 +0,0 @@
@ngdoc error
@name ngRepeat:dupes
@fullName Duplicate Key in Repeater
@description
Occurs if there are duplicate keys in an {@link api/ng.directive:ngRepeat ngRepeat} expression. Duplicate keys are banned because AngularJS uses keys to associate DOM nodes with items.
By default, collections are keyed by reference which is desirable for most common models but can be problematic for primitive types that are interned (share references).
For example the issue can be triggered by this *invalid* code:
```
<div ng-repeat="value in [4, 4]"></div>
```
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.
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>
```

View file

@ -1,12 +0,0 @@
@ngdoc error
@name ngRepeat:iexp
@fullName Invalid Expression
@description
Occurs when there is a syntax error in an {@link api/ng.directive:ngRepeat ngRepeat}'s expression. The expression should be in the form '_item_ in _collection_[ track by _id_]'.
Be aware, the ngRepeat directive parses the expression using a regex before sending _collection_ and optionally _id_ to the AngularJS parser. This error comes from the regex parsing.
To resolve, identify and fix errors in the expression, paying special attention to the 'in' and 'track by' keywords in the expression.
Please consult the api documentation of {@link api/ng.directive:ngRepeat ngRepeat} to learn more about valid syntax.

View file

@ -1,25 +0,0 @@
@ngdoc error
@name ngRepeat:iidexp
@fullName Invalid Identifier
@description
Occurs when there is an error in the identifier part of {@link api/ng.directive:ngRepeat ngRepeat}'s expression.
To resolve, use either a valid identifier or a tuple (_key_, _value_) where both _key_ and _value_ are valid identifiers.
Examples of *invalid* syntax:
```
<div ng-repeat="33 in users"></div>
<div ng-repeat="someFn() in users"></div>
<div ng-repeat="some user in users"></div>
```
Examples of *valid* syntax:
```
<div ng-repeat="user in users"></div>
<div ng-repeat="(id, user) in userMap"></div>
```
Please consult the api documentation of {@link api/ng.directive:ngRepeat ngRepeat} to learn more about valid syntax.

View file

@ -1,12 +0,0 @@
@ngdoc error
@name ngTransclude:orphan
@fullName Orphan ngTransclude Directive
@description
Occurs when an `ngTransclude` occurs without a transcluded ancestor element.
This error often occurs when you have forgotten to set `transclude: true` in some directive definition, and then used `ngTransclude` in the directive's template.
To resolve, either remove the offending `ngTransclude` or check that `transclude: true` is included in the intended directive definition.
Consult the API documentation for {@link guide/directive writing directives} to learn more.

View file

@ -1,16 +0,0 @@
@ngdoc error
@name $parse:isecdom
@fullName Referencing a DOM node in Expression
@description
Occurs when an expression attempts to access a DOM node.
AngularJS restricts access to DOM nodes from within expressions since it's a known way to
execute arbitrary Javascript code.
This check is only performed on object index and function calls in Angular expressions. These are
places that are harder for the developer to guard. Dotted member access (such as a.b.c) does not
perform this check - it's up to the developer to not expose such sensitive and powerful objects
directly on the scope chain.
To resolve this error, avoid access to DOM nodes.

View file

@ -1,18 +0,0 @@
@ngdoc error
@name $parse:isecfld
@fullName Referencing 'constructor' Field in Expression
@description
Occurs when an expression attempts to access an objects constructor field.
AngularJS bans constructor access from within expressions since constructor
access is a known way 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.
Example expression that would result in this error:
```
<div>{{user.constructor.name}}</div>
```

View file

@ -1,10 +0,0 @@
@ngdoc error
@name $parse:isecfn
@fullName Referencing Function Disallowed
@description
Occurs when an expression attempts to access the 'Function' object (constructor for all functions in JavaScript).
Angular bans access to Function from within expressions since constructor access is a known way to execute arbitrary Javascript code.
To resolve this error, avoid Function access.

View file

@ -1,16 +0,0 @@
@ngdoc error
@name $parse:isecwindow
@fullName Referencing Window object in Expression
@description
Occurs when an expression attempts to access a Window object.
AngularJS restricts access to the Window object from within expressions since it's a known way to
execute arbitrary Javascript code.
This check is only performed on object index and function calls in Angular expressions. These are
places that are harder for the developer to guard. Dotted member access (such as a.b.c) does not
perform this check - it's up to the developer to not expose such sensitive and powerful objects
directly on the scope chain.
To resolve this error, avoid Window access.

View file

@ -1,10 +0,0 @@
@ngdoc error
@name $parse:lexerr
@fullName Lexer Error
@description
Occurs when an expression has a lexical error, for example a malformed number (0.5e-) or an invalid unicode escape.
The error message contains a more precise error.
To resolve, learn more about {@link guide/expression Angular expressions}, identify the error and fix the expression's syntax.

View file

@ -1,9 +0,0 @@
@ngdoc error
@name $parse:syntax
@fullName Syntax Error
@description
Occurs when there is a syntax error in an expression. These errors are thrown while compiling the expression.
The error message contains a more precise description of the error, including the location (column) in the expression where the error occurred.
To resolve, learn more about {@link guide/expression Angular expressions}, identify the error and fix the expression's syntax.

View file

@ -1,9 +0,0 @@
@ngdoc error
@name $parse:ueoe
@fullName Unexpected End of Expression
@description
Occurs when an expression is missing tokens at the end of the expression.
For example, forgetting a closing bracket in an expression will trigger this error.
To resolve, learn more about {@link guide/expression Angular expressions}, identify the error and fix the expression's syntax.

View file

@ -1,9 +0,0 @@
@ngdoc error
@name $resource:badargs
@fullName Too Many Arguments
@description
This error occurs when specifying too many arguments to a {@link api/ngResource.$resource `$resource`} action, such as `get`, `query` or any user-defined custom action.
These actions may take up to 4 arguments.
For more information, refer to the {@link api/ngResource.$resource `$resource`} API reference documentation.

View file

@ -1,11 +0,0 @@
@ngdoc error
@name $resource:badcfg
@fullName Response does not match configured parameter
@description
This error occurs when the {@link api/ngResource.$resource `$resource`} service expects a response that can be deserialized as an array, receives an object, or vice versa.
By default, all resource actions expect objects, except `query` which expects arrays.
To resolve this error, make sure your `$resource` configuration matches the actual format of the data returned from the server.
For more information, see the {@link api/ngResource.$resource `$resource`} API reference documentation.

View file

@ -1,27 +0,0 @@
@ngdoc error
@name $resource:badmember
@fullName Syntax error in param value using @member lookup
@description
Occurs when there is a syntax error when attempting to extract a param
value from the data object.
Here's an example of valid syntax for `params` or `paramsDefault`:
````javascript
{
bar: '@foo.bar'
}
````
The part following the `@`, `foo.bar` in this case, should be a simple
dotted member lookup using only ASCII identifiers. This error occurs
when there is an error in that expression. The following are all syntax
errors
| Value | Error |
|---------|----------------|
| `@` | Empty expression following `@`. |
| `@1.a` | `1` is an invalid javascript identifier. |
| `@.a` | Leading `.` is invalid. |
| `@a[1]` | Only dotted lookups are supported (no index operator) |

View file

@ -1,8 +0,0 @@
@ngdoc error
@name $resource:badname
@fullName Cannot use hasOwnProperty as a parameter name
@description
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.

View file

@ -1,17 +0,0 @@
@ngdoc error
@name $rootScope:infdig
@fullName Infinite $digest Loop
@description
This error occurs when the application's model becomes unstable and each `$digest` cycle triggers a state change and subsequent `$digest` cycle.
Angular detects this situation and prevents an infinite loop from causing the browser to become unresponsive.
For example, the situation can occur by setting up a watch on a path and subsequently updating the same path when the value changes.
```
$scope.$watch('foo', function() {
$scope.foo = $scope.foo + 1;
});
```
The maximum number of allowed iterations of the `$digest` cycle is controlled via TTL setting which can be configured via {@link api/ng.$rootScopeProvider $rootScopeProvider}.

View file

@ -1,74 +0,0 @@
@ngdoc error
@name $rootScope:inprog
@fullName Action Already In Progress
@description
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.
`$digest` or `$apply` are processing operational states of the Scope - data-structure in Angular that provides context for models and enables model mutation observation.
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:
```
function MyController() {
thirdPartyComponent.getData(function(someData) {
scope.$apply(function() {
scope.someData = someData;
});
});
}
```
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.
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.
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.
For example:
```
myApp.directive('myDirective', function() {
return {
link: function($scope, $element) {
function doSomeWork() {
$scope.$apply(function() {
// do work here, and update the model
};
}
$element.on('click', doSomeWork);
doSomeWork(); // << this will throw an exception because templates are compiled within $apply
}
}
});
```
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
}
$element.on('click', function() {
$scope.$apply(doSomeWork); // <<< the $apply call was moved to the callsite that doesn't execute in $apply call already
});
doSomeWork();
}
}
});
```
To learn more about Angular processing model please check out the {@link guide/concepts concepts doc} as well as the {@link api/ng.$rootScope.Scope api} doc.

View file

@ -1,11 +0,0 @@
@ngdoc error
@name $sanitize:badparse
@fullName Parsing Error while Sanitizing
@description
This error occurs when the HTML string passed to '$sanitize' can't be parsed by the sanitizer.
The error contains part of the html string that can't be parsed.
The parser is more strict than a typical browser parser, so it's possible that some obscure input would produce this error despite the string being recognized as valid HTML by a browser.
If a valid html code results in this error, please file a bug.

View file

@ -1,8 +0,0 @@
@ngdoc error
@name $sce:icontext
@fullName Invalid / Unknown SCE context
@description
The context enum passed to {@link api/ng.$sce#methods_trustAs $sce.trustAs} was not recognized.
Please consult the list of {@link api/ng.$sce#contexts supported Strict Contextual Escaping (SCE) contexts}.

View file

@ -1,15 +0,0 @@
@ngdoc error
@name $sce:iequirks
@fullName IE8 in quirks mode is unsupported
@description
This error occurs when you are using AngularJS with {@link api/ng.$sce Strict Contextual Escaping (SCE)} mode enabled (the default) on IE8 or lower in quirks mode.
In this mode, IE8 allows one to execute arbitrary javascript by the use of the `expression()` syntax and is not supported.
Refer {@link http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx MSDN Blogs > IEBlog > Ending Expressions} to learn more about them.
To resolve this error please specify the proper doctype at the top of your main html document:
```
<!doctype html>
```

View file

@ -1,9 +0,0 @@
@ngdoc error
@name $sce:imatcher
@fullName Invalid matcher (only string patterns and RegExp instances are supported)
@description
Please see {@link api/ng.$sceDelegateProvider#methods_resourceUrlWhitelist
$sceDelegateProvider.resourceUrlWhitelist} and {@link
api/ng.$sceDelegateProvider#methods_resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} for the
list of acceptable items.

View file

@ -1,26 +0,0 @@
@ngdoc error
@name $sce:insecurl
@fullName Processing of a Resource from Untrusted Source Blocked
@description
AngularJS' {@link api/ng.$sce Strict Contextual Escaping (SCE)} mode (enabled by default) has blocked loading a resource from an insecure URL.
Typically, this would occur if you're attempting to load an Angular template from an untrusted source.
It's also possible that a custom directive threw this error for a similar reason.
Angular only loads templates from trusted URLs (by calling {@link api/ng.$sce#methods_getTrustedResourceUrl $sce.getTrustedResourceUrl} on the template URL).
By default, only URLs that belong to the same origin are trusted. These are urls with the same domain and protocol as the application document.
The {@link api/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
api/ng.$sceDelegateProvider#methods_resourceUrlWhitelist whitelist}/ {@link
api/ng.$sceDelegateProvider#methods_resourceUrlBlacklist blacklist} or wrap the URL with a call to {@link
api/ng.$sce#methods_trustAsResourceUrl $sce.trustAsResourceUrl}.
**Note**: The browser's {@link
https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest Same Origin
Policy} and {@link http://www.w3.org/TR/cors/ Cross-Origin Resource Sharing (CORS)} policy apply
that may further restrict whether the template is successfully loaded. (e.g. neither cross-domain
requests won't work on all browsers nor `file://` requests on some browsers)

View file

@ -1,8 +0,0 @@
@ngdoc error
@name $sce:itype
@fullName String Value is Required for SCE Trust Call
@description
{@link api/ng.$sce#methods_trustAs $sce.trustAs} requires a string value.
Read more about {@link api/ng.$sce Strict Contextual Escaping (SCE)} in AngularJS.

View file

@ -1,9 +0,0 @@
@ngdoc error
@name $sce:iwcard
@fullName The sequence *** is not a valid pattern wildcard
@description
The strings in {@link api/ng.$sceDelegateProvider#methods_resourceUrlWhitelist
$sceDelegateProvider.resourceUrlWhitelist} and {@link
api/ng.$sceDelegateProvider#methods_resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist} may not
contain the undefined sequence `***`. Only `*` and `**` wildcard patterns are defined.

View file

@ -1,15 +0,0 @@
@ngdoc error
@name $sce:unsafe
@fullName Require a safe/trusted value
@description
The value provided for use in a specific context was not found to be safe/trusted for use.
Angular's {@link api/ng.$sce Strict Contextual Escaping (SCE)} mode
(enabled by default), requires bindings in certain
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
api/ng.$sce Strict Contextual Escaping (SCE)}

View file

@ -1,289 +0,0 @@
@ngdoc overview
@name Developer Guide: Animations
@description
# Animations
AngularJS 1.2 provides animation hooks for common directives such as `ngRepeat`, `ngSwitch`, and `ngView`, as well as custom directives
via the `$animate` service. These animation hooks are set in place to trigger animations during the life cycle of various directives and when
triggered, will attempt to perform a CSS Transition, CSS Keyframe Animation or a JavaScript callback Animation (depending on if an animation is
placed on the given directive). Animations can be placed using vanilla CSS by following the naming conventions set in place by AngularJS
or with JavaScript code when it's defined as a factory.
Animations are not available unless you include the {@link api/ngAnimate `ngAnimate` module} as a dependency within your application.
Below is a quick example of animations being enabled for `ngShow` and `ngHide`:
<example animations="true">
<file name="index.html">
<div ng-init="checked=true">
<label>
<input type="checkbox" ng-model="checked" style="float:left; margin-right:10px;"> Is Visible...
</label>
<div class="check-element sample-show-hide" ng-show="checked" style="clear:both;">
Visible...
</div>
</div>
</file>
<file name="animations.css">
.sample-show-hide {
padding:10px;
border:1px solid black;
background:white;
}
.sample-show-hide.ng-hide-add, .sample-show-hide.ng-hide-remove {
-webkit-transition:all linear 0.5s;
-moz-transition:all linear 0.5s;
-o-transition:all linear 0.5s;
transition:all linear 0.5s;
display:block!important;
}
.sample-show-hide.ng-hide-add.ng-hide-add-active,
.sample-show-hide.ng-hide-remove {
opacity:0;
}
.sample-show-hide.ng-hide-add,
.sample-show-hide.ng-hide-remove.ng-hide-remove-active {
opacity:1;
}
</file>
</example>
## Installation
See the {@link api/ngAnimate API docs for `ngAnimate`} for instructions on installing the module.
You may also want to setup a separate CSS file for defining CSS-based animations.
## How they work
Animations in AngularJS are completely based on CSS classes. As long as you have a CSS class attached to a HTML element within
your website, you can apply animations to it. Lets say for example that we have an HTML template with a repeater in it like so:
<pre>
<div ng-repeat="item in items" class="repeated-item">
{{ item.id }}
</div>
</pre>
As you can see, the `.repeated-item` class is present on the element that will be repeated and this class will be
used as a reference within our application's CSS and/or JavaScript animation code to tell AngularJS to perform an animation.
As ngRepeat does its thing, each time a new item is added into the list, ngRepeat will add
a `ng-enter` class name to the element that is being added. When removed it will apply a `ng-leave` class name and when moved around
it will apply a `ng-move` class name.
Taking a look at the following CSS code, we can see some transition and keyframe animation code set for each of those events that
occur when ngRepeat triggers them:
<pre>
&#47;&#42;
We're using CSS transitions for when
the enter and move events are triggered
for the element that has the .repeated-item
class
&#42;&#47;
.repeated-item.ng-enter, .repeated-item.ng-move {
-webkit-transition:0.5s linear all;
-moz-transition:0.5s linear all;
-o-transition:0.5s linear all;
transition:0.5s linear all;
opacity:0;
}
&#47;&#42;
The ng-enter-active and ng-move-active
are where the transition destination properties
are set so that the animation knows what to
animate.
&#42;&#47;
.repeated-item.ng-enter.ng-enter-active,
.repeated-item.ng-move.ng-move-active {
opacity:1;
}
&#47;&#42;
We're using CSS keyframe animations for when
the leave event is triggered for the element
that has the .repeated-item class
&#42;&#47;
.repeated-item.ng-leave {
-webkit-animation:0.5s my_animation;
-moz-animation:0.5s my_animation;
-o-animation:0.5s my_animation;
animation:0.5s my_animation;
}
&#64;keyframes my_animation {
from { opacity:1; }
to { opacity:0; }
}
&#47;&#42;
Unfortunately each browser vendor requires
its own definition of keyframe animation code...
&#42;&#47;
&#64;-webkit-keyframes my_animation {
from { opacity:1; }
to { opacity:0; }
}
&#64;-moz-keyframes my_animation {
from { opacity:1; }
to { opacity:0; }
}
&#64;-o-keyframes my_animation {
from { opacity:1; }
to { opacity:0; }
}
</pre>
The same approach to animation can be used using JavaScript code (**jQuery is used within to perform animations**):
<pre>
myModule.animation('.repeated-item', function() {
return {
enter : function(element, done) {
element.css('opacity',0);
jQuery(element).animate({
opacity: 1
}, done);
// optional onDone or onCancel callback
// function to handle any post-animation
// cleanup operations
return function(isCancelled) {
if(isCancelled) {
jQuery(element).stop();
}
}
},
leave : function(element, done) {
element.css('opacity', 1);
jQuery(element).animate({
opacity: 0
}, done);
// optional onDone or onCancel callback
// function to handle any post-animation
// cleanup operations
return function(isCancelled) {
if(isCancelled) {
jQuery(element).stop();
}
}
},
move : function(element, done) {
element.css('opacity', 0);
jQuery(element).animate({
opacity: 1
}, done);
// optional onDone or onCancel callback
// function to handle any post-animation
// cleanup operations
return function(isCancelled) {
if(isCancelled) {
jQuery(element).stop();
}
}
},
// you can also capture these animation events
addClass : function(element, className, done) {},
removeClass : function(element, className, done) {}
}
});
</pre>
With these generated CSS class names present on the element at the time, AngularJS automatically
figures out whether to perform a CSS and/or JavaScript animation. If both CSS and JavaScript animation
code is present, and match the CSS class name on the element, then AngularJS will run both animations at the same time.
## Class and ngClass animation hooks
AngularJS also pays attention to CSS class changes on elements by triggering the **add** and **remove** hooks.
This means that if a CSS class is added to or removed from an element then an animation can be executed in between
before the CSS class addition or removal is finalized. (Keep in mind that AngularJS will only be
able to capture class changes if an **expression** or the **ng-class** directive is used on the element.)
The example below shows how to perform animations during class changes:
<example animations="true">
<file name="index.html">
<p>
<input type="button" value="set" ng-click="myCssVar='css-class'">
<input type="button" value="clear" ng-click="myCssVar=''">
<br>
<span ng-class="myCssVar">CSS-Animated Text</span>
</p>
</file>
<file name="style.css">
.css-class-add, .css-class-remove {
-webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
-moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
-o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
}
.css-class,
.css-class-add.css-class-add-active {
color: red;
font-size:3em;
}
.css-class-remove.css-class-remove-active {
font-size:1.0em;
color:black;
}
</file>
</example>
Although the CSS is a little different then what we saw before, the idea is the same.
## Which directives support animations?
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 api/ng.directive:ngRepeat#usage_animations ngRepeat} | enter, leave, and move |
| {@link api/ngRoute.directive:ngView#usage_animations ngView} | enter and leave |
| {@link api/ng.directive:ngInclude#usage_animations ngInclude} | enter and leave |
| {@link api/ng.directive:ngSwitch#usage_animations ngSwitch} | enter and leave |
| {@link api/ng.directive:ngIf#usage_animations ngIf} | enter and leave |
| {@link api/ng.directive:ngClass#usage_animations ngClass or &#123;&#123;class&#125;&#125;} | add and remove |
| {@link api/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 api/ngAnimate.$animate API docs}.
## How do I use animations in my own directives?
Animations within custom directives can also be established by injecting `$animate` directly into your directive and
making calls to it when needed.
<pre>
myModule.directive('my-directive', ['$animate', function($animate) {
return function(element, scope, attrs) {
element.bind('click', function() {
if(element.hasClass('clicked')) {
$animate.removeClass(element, 'clicked');
} else {
$animate.addClass(element, 'clicked');
}
});
};
}]);
</pre>
## More about animations
For a full breakdown of each method available on `$animate`, see the {@link api/ngAnimate.$animate API documentation}.
To see a complete demo, see the {@link tutorial/step_12 animation step within the AngularJS phonecat tutorial}.

View file

@ -7,7 +7,8 @@
This page explains the Angular initialization process and how you can manually initialize Angular
if necessary.
## Angular `<script>` Tag
# Angular `<script>` Tag
This example shows the recommended path for integrating Angular with what we call automatic
initialization.
@ -37,10 +38,6 @@ initialization.
<html ng-app>
* If IE7 support is required add `id="ng-app"`
<html ng-app id="ng-app">
* If you choose to use the old style directive syntax `ng:` then include xml-namespace in `html`
to make IE happy. (This is here for historical reasons, and we no longer recommend use of
`ng:`.)
@ -49,9 +46,7 @@ initialization.
## Automatic Initialization
<img class="pull-right" style="padding-left: 3em;" src="img/guide/concepts-startup.png">
# Automatic Initialization
Angular initializes automatically upon `DOMContentLoaded` event or when the `angular.js` script is
evaluated if at that time `document.readyState` is set to `'complete'`. At this point Angular looks
@ -77,14 +72,16 @@ If the {@link api/ng.directive:ngApp `ng-app`} directive is found then Angular w
## Manual Initialization
# Manual Initialization
If you need to have more control over the initialization process, you can use a manual
bootstrapping method instead. Examples of when you'd need to do this include using script loaders
or the need to perform an operation before Angular compiles a page.
Here is an example of manually initializing Angular:
Here is an example of manually initializing Angular. The example is equivalent to using the {@link
api/ng.directive:ngApp ng-app} directive.
<pre>
<!doctype html>
@ -94,37 +91,17 @@ Here is an example of manually initializing Angular:
<script src="http://code.angularjs.org/angular.js"></script>
<script>
angular.element(document).ready(function() {
angular.module('myApp', []);
angular.bootstrap(document, ['myApp']);
angular.bootstrap(document);
});
</script>
</body>
</html>
</pre>
Note that we have provided the name of our application module to be loaded into the injector as the second
parameter of the {@link api/angular.bootstrap} function. Notice that `angular.bootstrap` will not create modules
on the fly. You must create any custom {@link guide/module modules} before you pass them as a parameter.
This is the sequence that your code should follow:
1. After the page and all of the code is loaded, find the root element of your AngularJS
application, which is typically the root of the document.
1. After the page and all of the code is loaded, find the root of the HTML template, which is
typically the root of the document.
2. Call {@link api/angular.bootstrap} to {@link compiler compile} the element into an
2. Call {@link api/angular.bootstrap} to {@link compiler compile} the template into an
executable, bi-directionally bound application.
## Deferred Bootstrap
This feature enables tools like Batarang and test runners to
hook into angular's bootstrap process and sneak in more modules
into the DI registry which can replace or augment DI services for
the purpose of instrumentation or mocking out heavy dependencies.
If `window.name` contains prefix `NG_DEFER_BOOTSTRAP!` when
{@link api/angular.bootstrap} is called, the bootstrap process will be paused
until `angular.resumeBootstrap()` is called.
`angular.resumeBootstrap()` takes an optional array of modules that
should be added to the original list of modules that the app was
about to be bootstrapped with.

View file

@ -2,44 +2,35 @@
@name Developer Guide: HTML Compiler
@description
<div class="alert alert-warning">
**Note:** this guide is targeted towards developers who are already familiar with AngularJS basics.
If you're just getting started, we recommend the {@link tutorial/ tutorial} first.
If you just want to create custom directives, we recommend the {@link guide/directive directives guide}.
If you want a deeper look into Angular's compilation process, you're in the right place.
</div>
# Overview
Angular's {@link api/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
and even create new HTML elements or attributes with custom behavior. Angular calls these behavior
extensions {@link api/ng.$compileProvider#methods_directive directives}.
and even create new HTML element or attributes with custom behavior. Angular calls these behavior
extensions {@link api/ng.$compileProvider#directive directives}.
HTML has a lot of constructs for formatting the HTML for static documents in a declarative fashion.
For example if something needs to be centered, there is no need to provide instructions to the
browser how the window size needs to be divided in half so that the center is found, and that this
center needs to be aligned with the text's center. Simply add an `align="center"` attribute to any
browser how the window size needs to be divided in half so that center is found, and that this
center needs to be aligned with the text's center. Simply add `align="center"` attribute to any
element to achieve the desired behavior. Such is the power of declarative language.
But the declarative language is also limited, since it does not allow you to teach the browser new
syntax. For example there is no easy way to get the browser to align the text at 1/3 the position
instead of 1/2. What is needed is a way to teach the browser new HTML syntax.
instead of 1/2. What is needed is a way to teach browser new HTML syntax.
Angular comes pre-bundled with common directives which are useful for building any app. We also
expect that you will create directives that are specific to your app. These extensions become a
expect that you will create directives that are specific to your app. These extension become a
Domain Specific Language for building your application.
All of this compilation takes place in the web browser; no server side or pre-compilation step is
involved.
## Compiler
# Compiler
Compiler is an Angular service which traverses the DOM looking for attributes. The compilation
process happens in two phases.
Compiler is an angular service which traverses the DOM looking for attributes. The compilation
process happens into two phases.
1. **Compile:** traverse the DOM and collect all of the directives. The result is a linking
function.
@ -48,16 +39,17 @@ process happens in two phases.
scope model are reflected in the view, and any user interactions with the view are reflected
in the scope model. This makes the scope model the single source of truth.
Some directives such as {@link api/ng.directive:ngRepeat `ng-repeat`} clone DOM elements once
for each item in a collection. Having a compile and link phase improves performance since the
cloned template only needs to be compiled once, and then linked once for each clone instance.
Some directives such {@link api/ng.directive:ngRepeat
`ng-repeat`} clone DOM elements once for each item in collection. Having a compile and link phase
improves performance since the cloned template only needs to be compiled once, and then linked
once for each clone instance.
## Directive
# Directive
A directive is a behavior which should be triggered when specific HTML constructs are encountered
during the compilation process. The directives can be placed in element names, attributes, class
names, as well as comments. Here are some equivalent examples of invoking the {@link
A directive is a behavior which should be triggered when specific HTML constructs are encountered in
the compilation process. The directives can be placed in element names, attributes, class names, as
well as comments. Here are some equivalent examples of invoking the {@link
api/ng.directive:ngBind `ng-bind`} directive.
<pre>
@ -68,7 +60,7 @@ api/ng.directive:ngBind `ng-bind`} directive.
</pre>
A directive is just a function which executes when the compiler encounters it in the DOM. See {@link
api/ng.$compileProvider#methods_directive directive API} for in-depth documentation on how
api/ng.$compileProvider#directive directive API} for in-depth documentation on how
to write directives.
Here is a directive which makes any element draggable. Notice the `draggable` attribute on the
@ -78,21 +70,19 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
<file name="script.js">
angular.module('drag', []).
directive('draggable', function($document) {
var startX=0, startY=0, x = 0, y = 0;
return function(scope, element, attr) {
var startX = 0, startY = 0, x = 0, y = 0;
element.css({
position: 'relative',
border: '1px solid red',
backgroundColor: 'lightgrey',
cursor: 'pointer'
});
element.on('mousedown', function(event) {
// Prevent default dragging of selected content
event.preventDefault();
element.bind('mousedown', function(event) {
startX = event.screenX - x;
startY = event.screenY - y;
$document.on('mousemove', mousemove);
$document.on('mouseup', mouseup);
$document.bind('mousemove', mousemove);
$document.bind('mouseup', mouseup);
});
function mousemove(event) {
@ -117,265 +107,35 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
</example>
The presence of the `draggable` attribute on any element gives the element new behavior.
We extended the vocabulary of the browser in a way which is natural to anyone who is familiar with the principles of HTML.
The presence of the `draggable` attribute on any element gives the element new behavior. The beauty of
this approach is that we have taught the browser a new trick. We have extended the vocabulary of
what the browser understands in a way which is natural to anyone who is familiar with HTML
principles.
## Understanding View
# Understanding View
Most other templating systems consume a static string template and
There are many templating systems out there. Most of them consume a static string template and
combine it with data, resulting in a new string. The resulting text is then `innerHTML`ed into
an element.
<img src="img/One_Way_Data_Binding.png">
This means that any changes to the data need to be re-merged with the template and then
`innerHTML`ed into the DOM. Some of the issues with this approach are:
`innerHTML`ed into the DOM. Some of the issues with this approach are: reading user input and merging it with data,
clobbering user input by overwriting it, managing the whole update process, and lack of behavior
expressiveness.
1. reading user input and merging it with data
2. clobbering user input by overwriting it
3. managing the whole update process
4. lack of behavior expressiveness
Angular is different. The Angular compiler consumes the DOM, not string templates.
Angular is different. The Angular compiler consumes the DOM with directives, not string templates.
The result is a linking function, which when combined with a scope model results in a live view. The
view and scope model bindings are transparent. The developer does not need to make any special calls to update
the view. And because `innerHTML` is not used, you won't accidentally clobber user input.
view and scope model bindings are transparent. No action from the developer is needed to update
the view. And because no `innerHTML` is used there are no issues of clobbering user input.
Furthermore, Angular directives can contain not just text bindings, but behavioral constructs as
well.
<img src="img/Two_Way_Data_Binding.png">
The Angular approach produces a stable DOM. The DOM element instance bound to a model
The Angular approach produces a stable DOM. This means that the DOM element instance bound to a model
item instance does not change for the lifetime of the binding. This means that the code can get
hold of the elements and register event handlers and know that the reference will not be destroyed
by template data merge.
## How directives are compiled
It's important to note that Angular operates on DOM nodes rather than strings. Usually, you don't
notice this restriction because when a page loads, the web browser parses HTML into the DOM automatically.
However it's important to keep this in mind when calling `$compile` yourself, because passing it a string
will fail. Instead, use `angular.element` to convert a string to DOM before passing elements into
Angular's `$compile` service.
HTML compilation happens in three phases:
1. {@link api/ng.$compile `$compile`} traverses the DOM and matches directives.
If the compiler finds that an element matches a directive, then the directive is added to the list of
directives that match the DOM element. A single element may match multiple directives.
2. Once all directives matching a DOM element have been identified, the compiler sorts the directives
by their `priority`.
Each directive's `compile` functions are executed. Each `compile` function has a chance to
modify the DOM. Each `compile` function returns a `link` function. These functions are composed into
a "combined" link function, which invokes each directive's returned `link` function.
3. `$compile` links the template with the scope by calling the combined linking function from the previous step.
This in turn will call the linking function of the individual directives, registering listeners on the elements
and setting up {@link api/ng.$rootScope.Scope#methods_$watch `$watch`s} with the {@link api/ng.$rootScope.Scope `scope`}
as each directive is configured to do.
The result of this is a live binding between the scope and the DOM. So at this point, a change in
a model on the compiled scope will be reflected in the DOM.
Below is the corresponding code using the `$compile` service.
This should help give you an idea of what Angular does internally.
<pre>
var $compile = ...; // injected into your code
var scope = ...;
var parent = ...; // DOM element where the compiled template can be appended
var html = '<div ng-bind="exp"></div>';
// Step 1: parse HTML into DOM element
var template = angular.element(html);
// Step 2: compile the template
var linkFn = $compile(template);
// Step 3: link the compiled template with the scope.
var element = linkFn(scope);
// Step 4: Append to DOM (optional)
parent.appendChild(element);
</pre>
### The difference between Compile and Link
At this point you may wonder why the compile process has separate compile and link phases. The
short answer is that compile and link separation is needed any time a change in a model causes
a change in the **structure** of the DOM.
It's rare for directives to have a **compile function**, since most directives are concerned with
working with a specific DOM element instance rather than changing its overall structure.
Directives often have a **link function**. A link function allows the directive to register
listeners to the specific cloned DOM element instance as well as to copy content into the DOM
from the scope.
<div class="alert alert-success">
**Best Practice:** Any operation which can be shared among the instance of directives should be
moved to the compile function for performance reasons.
</div>
#### An Example of "Compile" Versus "Link"
To understand, let's look at a real-world example with `ngRepeat`:
<pre>
Hello {{user}}, you have these actions:
<ul>
<li ng-repeat="action in user.actions">
{{action.description}}
</li>
</ul>
</pre>
When the above example is compiled, the compiler visits every node and looks for directives.
`{{user}}` matches the {@link api/ng.$interpolate interpolation directive}
and `ng-repeat` matches the {@link api/ng.directive:ngRepeat `ngRepeat` directive}.
But {@link api/ng.directive:ngRepeat ngRepeat} has a dilemma.
It needs to be able to clone new `<li>` elements for every `action` in `user.actions`.
This initially seems trivial, but it becomes more complicated when you consider that `user.actions`
might have items added to it later. This means that it needs to save a clean copy of the `<li>`
element for cloning purposes.
As new `action`s are inserted, the template `<li>` element needs to be cloned and inserted into `ul`.
But cloning the `<li>` element is not enough. It also needs to compile the `<li>` so that its
directives, like `{{action.description}}`, evaluate against the right {@link api/ng.$rootScope.Scope scope}.
A naive approach to solving this problem would be to simply insert a copy of the `<li>` element and
then compile it.
The problem with this approach is that compiling on every `<li>` element that we clone would duplicate
a lot of the work. Specifically, we'd be traversing `<li>` each time before cloning it to find the
directives. This would cause the compilation process to be slower, in turn making applications
less responsive when inserting new nodes.
The solution is to break the compilation process into two phases:
the **compile phase** where all of the directives are identified and sorted by priority,
and a **linking phase** where any work which "links" a specific instance of the
{@link api/ng.$rootScope.Scope scope} and the specific instance of an `<li>` is performed.
<div class="alert alert-warning">
**Note:** *Link* means setting up listeners on the DOM and setting up `$watch` on the Scope to
keep the two in sync.
</div>
{@link api/ng.directive:ngRepeat `ngRepeat`} works by preventing the compilation process from
descending into the `<li>` element so it can make a clone of the original and handle inserting
and removing DOM nodes itself.
Instead the {@link api/ng.directive:ngRepeat `ngRepeat`} directive compiles `<li>` separately.
The result of the `<li>` element compilation is a linking function which contains all of the
directives contained in the `<li>` element, ready to be attached to a specific clone of the `<li>`
element.
At runtime the {@link api/ng.directive:ngRepeat `ngRepeat`} watches the expression and as items
are added to the array it clones the `<li>` element, creates a new
{@link api/ng.$rootScope.Scope scope} for the cloned `<li>` element and calls the link function
on the cloned `<li>`.
### Understanding How Scopes Work with Transcluded Directives
One of the most common use cases for directives is to create reusable components.
Below is a pseudo code showing how a simplified dialog component may work.
<pre>
<div>
<button ng-click="show=true">show</button>
<dialog title="Hello {{username}}."
visible="show"
on-cancel="show = false"
on-ok="show = false; doSomething()">
Body goes here: {{username}} is {{title}}.
</dialog>
</div>
</pre>
Clicking on the "show" button will open the dialog. The dialog will have a title, which is
data bound to `username`, and it will also have a body which we would like to transclude
into the dialog.
Here is an example of what the template definition for the `dialog` widget may look like.
<pre>
<div ng-show="visible">
<h3>{{title}}</h3>
<div class="body" ng-transclude></div>
<div class="footer">
<button ng-click="onOk()">Save changes</button>
<button ng-click="onCancel()">Close</button>
</div>
</div>
</pre>
This will not render properly, unless we do some scope magic.
The first issue we have to solve is that the dialog box template expects `title` to be defined.
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 `locals` to create local variables which the template
expects as follows:
<pre>
scope: {
title: '@', // the title uses the data-binding from the parent scope
onOk: '&', // create a delegate onOk function
onCancel: '&', // create a delegate onCancel function
visible: '=' // set up visible to accept data-binding
}
</pre>
Creating local properties on widget scope creates two problems:
1. isolation - if the user forgets to set `title` attribute of the dialog widget the dialog
template will bind to parent scope property. This is unpredictable and undesirable.
2. transclusion - the transcluded DOM can see the widget locals, which may overwrite the
properties which the transclusion needs for data-binding. In our example the `title`
property of the widget clobbers the `title` property of the transclusion.
To solve the issue of lack of isolation, the directive declares a new `isolated` scope. An
isolated scope does not prototypically inherit from the parent scope, and therefore we don't have
to worry about accidentally clobbering any properties.
However `isolated` scope creates a new problem: if a transcluded DOM is a child of the widget
isolated scope then it will not be able to bind to anything. For this reason the transcluded scope
is a child of the original scope, before the widget created an isolated scope for its local
variables. This makes the transcluded and widget isolated scope siblings.
This may seem to be unexpected complexity, but it gives the widget user and developer the least
surprise.
Therefore the final directive definition looks something like this:
<pre>
transclude: true,
scope: {
title: '@', // the title uses the data-binding from the parent scope
onOk: '&', // create a delegate onOk function
onCancel: '&', // create a delegate onCancel function
visible: '=' // set up visible to accept data-binding
},
restrict: 'E',
replace: true
</pre>

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