Compare commits
126 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d396f42537 | |||
| 5ec900a125 | |||
| 66503a6e18 | |||
| 2d8749e8c9 | |||
| b39e1d47b9 | |||
| 5061d2c97c | |||
| a961291aa2 | |||
| 3068d8e52a | |||
| d2dd3581a9 | |||
| 53feb272be | |||
| 408508ad29 | |||
| ece7d19115 | |||
| 992101da10 | |||
| 47f42ecf45 | |||
| fd995abc9a | |||
| 5f9a9747d2 | |||
| 94b0f2d35d | |||
| c12e8d4665 | |||
| c65796d496 | |||
| 34b43eab5f | |||
| 9d9cdfb575 | |||
| 68a09ba74d | |||
| 239d0b1f49 | |||
| 4e04c73cd3 | |||
| 6c863e5bba | |||
| e0cf7c5bf2 | |||
| 466320f691 | |||
| 7e02fa07eb | |||
| 9ed9777317 | |||
| e661bc9f15 | |||
| 7af210f1de | |||
| b48203f9f2 | |||
| 789f0f1809 | |||
| 8366d545c5 | |||
| 1f5b4c95eb | |||
| 910d652ce5 | |||
| e5e62a5479 | |||
| 8f14b726b0 | |||
| 88b6a9a288 | |||
| d4fc40c282 | |||
| 653700df5b | |||
| 2712c2f197 | |||
| 36e6de1d91 | |||
| 9bf964f1f3 | |||
| 54f0bc0fe0 | |||
| 2ece4d0347 | |||
| 1812af58c2 | |||
| 19cb2e3d12 | |||
| c92ce4511b | |||
| d7548fdf1c | |||
| 7e239f9485 | |||
| fa5daa7693 | |||
| 00456a8f93 | |||
| 0b3022e8e0 | |||
| 456026eff1 | |||
| 8ec3efd967 | |||
| 8528781f85 | |||
| 98f603722d | |||
| ebece0bcb9 | |||
| 4b7398eeca | |||
| 4e79decc30 | |||
| 888b0f5400 | |||
| aaf9c5e598 | |||
| 893d2f8000 | |||
| effc98fdc9 | |||
| 64cdbf3ae9 | |||
| 5bba892ffd | |||
| ec27deacfd | |||
| c6e4defcb6 | |||
| 74a7afcb31 | |||
| 08cc6edd38 | |||
| 4f3870500d | |||
| cd0507bc3a | |||
| 20eef05195 | |||
| a6d7b4bdbd | |||
| 5811c5c35b | |||
| e37e30e93f | |||
| a0b8ab8d7d | |||
| 129c53730c | |||
| 8695138aec | |||
| ed56872bb2 | |||
| cb183433a0 | |||
| 631fbda6a9 | |||
| 4c33b56612 | |||
| 93b0c2d892 | |||
| d262378b7c | |||
| 7729c84ec7 | |||
| dffeef29d7 | |||
| 2e22588ccf | |||
| 0b0acb0342 | |||
| 18fc43e828 | |||
| ed47d811e2 | |||
| a84f9f6178 | |||
| 4fbbe1152e | |||
| df3d941c57 | |||
| 2bd3214a55 | |||
| fc2abef327 | |||
| 76a0eb89fb | |||
| ee57b4c26b | |||
| 29eaabc000 | |||
| 2a6081057f | |||
| 79538afd7b | |||
| bcfa64e77c | |||
| b7a2deed30 | |||
| 98ff901bda | |||
| 428b81cba9 | |||
| 5dae9c230e | |||
| c3fad1157e | |||
| 9242c580a1 | |||
| c2860944c6 | |||
| e9a00fbdc3 | |||
| d018ac2a9a | |||
| b770c353bc | |||
| 812277c257 | |||
| 61871da9de | |||
| 9ee075518f | |||
| afe93eaff8 | |||
| d8f94d1a3f | |||
| f53b53df22 | |||
| eab5731afc | |||
| 986c446aaf | |||
| 60366c8d0b | |||
| 494c8aa0b3 | |||
| cd9459e129 | |||
| 2862883bd8 | |||
| bbb673a48a |
+1
-2
@@ -9,8 +9,7 @@ performance/temp*.html
|
||||
*.swp
|
||||
angular.js.tmproj
|
||||
/node_modules/
|
||||
/components/
|
||||
/bower_components/
|
||||
bower_components/
|
||||
angular.xcodeproj
|
||||
.idea
|
||||
.agignore
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ install:
|
||||
- npm config set spin false
|
||||
# Log HTTP requests
|
||||
- npm config set loglevel http
|
||||
- time ./scripts/travis/fetch_bundle.sh
|
||||
- time ./scripts/travis/npm-bundle-deps.sh
|
||||
- time npm install
|
||||
|
||||
before_script:
|
||||
|
||||
+1128
File diff suppressed because it is too large
Load Diff
@@ -15,7 +15,7 @@ it makes development fun!
|
||||
* Tutorial: http://docs.angularjs.org/tutorial
|
||||
* API Docs: http://docs.angularjs.org/api
|
||||
* Developer Guide: http://docs.angularjs.org/guide
|
||||
* Contribution guidelines: http://docs.angularjs.org/misc/contribute
|
||||
* Contribution guidelines: [CONTRIBUTING.md](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md)
|
||||
* Dashboard: http://dashboard.angularjs.org
|
||||
|
||||
Building AngularJS
|
||||
@@ -37,7 +37,7 @@ To execute end-to-end (e2e) tests, use:
|
||||
grunt test:e2e
|
||||
|
||||
To learn more about the grunt tasks, run `grunt --help` and also read our
|
||||
[contribution guidelines](http://docs.angularjs.org/misc/contribute).
|
||||
[contribution guidelines](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md).
|
||||
|
||||
|
||||
[](https://github.com/igrigorik/ga-beacon)
|
||||
|
||||
+1
-5
@@ -2,11 +2,7 @@
|
||||
"name": "AngularJS",
|
||||
"devDependencies": {
|
||||
"jquery": "1.10.2",
|
||||
"lunr.js": "0.4.3",
|
||||
"open-sans-fontface": "1.0.4",
|
||||
"google-code-prettify": "1.0.1",
|
||||
"closure-compiler": "https://closure-compiler.googlecode.com/files/compiler-20130603.zip",
|
||||
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip",
|
||||
"bootstrap": "3.1.1"
|
||||
"ng-closure-runner": "https://raw.github.com/angular/ng-closure-runner/v0.2.3/assets/ng-closure-runner.zip"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -635,12 +635,14 @@ ul.events > li {
|
||||
display:inline-block;
|
||||
padding:3px 0;
|
||||
}
|
||||
.nav-index-group .nav-index-listing:not(.nav-index-section) + .nav-index-listing:not(.nav-index-section):after {
|
||||
padding-right:5px;
|
||||
content:", ";
|
||||
.nav-index-group .nav-index-listing:not(.nav-index-section):after {
|
||||
padding-right:5px;
|
||||
margin-left:-3px;
|
||||
content:", ";
|
||||
}
|
||||
.nav-index-group .nav-index-listing:last-child {
|
||||
.nav-index-group .nav-index-listing:last-child:after {
|
||||
content:"";
|
||||
display:inline-block;
|
||||
}
|
||||
.nav-index-group .nav-index-section {
|
||||
display:block;
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.lang-text * {
|
||||
color: #333333!important;
|
||||
}
|
||||
|
||||
.pln {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ describe('docs.angularjs.org', function () {
|
||||
var nameInput = element(by.model('user.name'));
|
||||
nameInput.sendKeys('!!!');
|
||||
|
||||
var code = element(by.css('tt'));
|
||||
var code = element.all(by.css('tt')).first();
|
||||
expect(code.getText()).toContain('guest!!!');
|
||||
});
|
||||
|
||||
@@ -68,4 +68,21 @@ describe('docs.angularjs.org', function () {
|
||||
expect(element(by.css('.minerr-errmsg')).getText()).toEqual("Argument 'Missing' is not a function, got undefined");
|
||||
});
|
||||
});
|
||||
|
||||
describe("templates", function() {
|
||||
it("should show parameter defaults", function() {
|
||||
browser.get('index-debug.html#!/api/ng/service/$timeout');
|
||||
expect(element.all(by.css('.input-arguments p em')).first().getText()).toContain('(default: 0)');
|
||||
});
|
||||
});
|
||||
|
||||
describe("API pages", function() {
|
||||
it("should display links to code on GitHub", function() {
|
||||
browser.get('index-debug.html#!/api/ng/service/$http');
|
||||
expect(element(by.css('.improve-docs')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/edit\/.+\/src\/ng\/http\.js/);
|
||||
|
||||
browser.get('index-debug.html#!/api/ng/service/$http');
|
||||
expect(element(by.css('.view-source')).getAttribute('href')).toMatch(/https?:\/\/github\.com\/angular\/angular\.js\/tree\/.+\/src\/ng\/http\.js#L\d+/);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -74,6 +74,7 @@ angular.module('search', [])
|
||||
var index = lunrSearch(function() {
|
||||
this.ref('id');
|
||||
this.field('title', {boost: 50});
|
||||
this.field('members', { boost: 40});
|
||||
this.field('keywords', { boost : 20 });
|
||||
});
|
||||
|
||||
@@ -82,7 +83,8 @@ angular.module('search', [])
|
||||
index.store({
|
||||
id : key,
|
||||
title : page.searchTerms.titleWords,
|
||||
keywords : page.searchTerms.keywords
|
||||
keywords : page.searchTerms.keywords,
|
||||
members : page.searchTerms.members
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "AngularJS-docs-app",
|
||||
"dependencies": {
|
||||
"jquery": "2.1.1",
|
||||
"lunr.js": "0.4.3",
|
||||
"open-sans-fontface": "1.0.4",
|
||||
"google-code-prettify": "1.0.1",
|
||||
"bootstrap": "3.1.1"
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,8 @@ module.exports = function(config) {
|
||||
]);
|
||||
|
||||
config.append('processing.tagDefinitions', [
|
||||
require('./tag-defs/tutorial-step')
|
||||
require('./tag-defs/tutorial-step'),
|
||||
require('./tag-defs/sortOrder')
|
||||
]);
|
||||
|
||||
config.append('processing.defaultTagTransforms', [
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
"use strict";
|
||||
var _ = require('lodash');
|
||||
var log = require('winston');
|
||||
var fs = require('fs');
|
||||
@@ -5,7 +6,7 @@ var path = require('canonical-path');
|
||||
|
||||
module.exports = {
|
||||
name: 'keywords',
|
||||
runAfter: ['docs-processed'],
|
||||
runAfter: ['docs-processed', 'api-docs'],
|
||||
runBefore: ['adding-extra-docs'],
|
||||
description: 'This processor extracts all the keywords from the document',
|
||||
process: function(docs, config) {
|
||||
@@ -52,7 +53,7 @@ module.exports = {
|
||||
_.forEach(tokens, function(token){
|
||||
var match = token.match(KEYWORD_REGEX);
|
||||
if (match){
|
||||
key = match[1];
|
||||
var key = match[1];
|
||||
if ( !keywordMap[key]) {
|
||||
keywordMap[key] = true;
|
||||
words.push(key);
|
||||
@@ -69,17 +70,28 @@ module.exports = {
|
||||
|
||||
var words = [];
|
||||
var keywordMap = _.clone(ignoreWordsMap);
|
||||
var members = [];
|
||||
var membersMap = {};
|
||||
|
||||
// Search each top level property of the document for search terms
|
||||
_.forEach(doc, function(value, key) {
|
||||
|
||||
if ( _.isString(value) && !propertiesToIgnore[key] ) {
|
||||
extractWords(value, words, keywordMap);
|
||||
}
|
||||
|
||||
if ( key === 'methods' || key === 'properties' || key === 'events' ) {
|
||||
_.forEach(value, function(member) {
|
||||
extractWords(member.name, members, membersMap);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
doc.searchTerms = {
|
||||
titleWords: extractTitleWords(doc.name),
|
||||
keywords: _.sortBy(words).join(' ')
|
||||
keywords: _.sortBy(words).join(' '),
|
||||
members: _.sortBy(members).join(' ')
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -119,13 +119,20 @@ var navGroupMappers = {
|
||||
})];
|
||||
},
|
||||
pages: function(pages, area) {
|
||||
return [getNavGroup(pages, area, 'path', function(page) {
|
||||
return {
|
||||
name: page.name,
|
||||
href: page.path,
|
||||
type: 'page'
|
||||
};
|
||||
})];
|
||||
return [getNavGroup(
|
||||
pages,
|
||||
area,
|
||||
function(page) {
|
||||
return page.sortOrder || page.path;
|
||||
},
|
||||
function(page) {
|
||||
return {
|
||||
name: page.name,
|
||||
href: page.path,
|
||||
type: 'page'
|
||||
};
|
||||
}
|
||||
)];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -33,7 +33,8 @@ module.exports = {
|
||||
innerTest: file.fileContents,
|
||||
pathPrefix: '.', // Hold for if we test with full jQuery
|
||||
exampleId: example.id,
|
||||
description: example.doc.id
|
||||
description: example.doc.id,
|
||||
'ng-app-included': example['ng-app-included']
|
||||
};
|
||||
|
||||
if (env === 'jquery') {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
name: 'sortOrder',
|
||||
transforms: function(doc, tag, value) {
|
||||
return parseInt(value, 10);
|
||||
}
|
||||
};
|
||||
@@ -1,7 +1,10 @@
|
||||
describe("{$ doc.description $}", function() {
|
||||
var rootEl;
|
||||
beforeEach(function() {
|
||||
rootEl = browser.rootEl;{% if doc['ng-app-included'] %}
|
||||
browser.rootEl = '[ng-app]';{% endif %}
|
||||
browser.get("{$ doc.pathPrefix $}/{$ doc.examplePath $}");
|
||||
});
|
||||
|
||||
{% if doc['ng-app-included'] %}afterEach(function() { browser.rootEl = rootEl; });{% endif %}
|
||||
{$ doc.innerTest $}
|
||||
});
|
||||
|
||||
@@ -25,3 +25,33 @@ angular.module('myApp', [])
|
||||
// Do something with myService
|
||||
}]);
|
||||
```
|
||||
|
||||
An unknown provider error can also be caused by accidentally redefining a
|
||||
module using the `angular.module` API, as shown in the following example.
|
||||
|
||||
```
|
||||
angular.module('myModule', [])
|
||||
.service('myCoolService', function () { /* ... */ });
|
||||
|
||||
angular.module('myModule', [])
|
||||
// myModule has already been created! This is not what you want!
|
||||
.directive('myDirective', ['myCoolService', function (myCoolService) {
|
||||
// This directive definition throws unknown provider, because myCoolService
|
||||
// has been destroyed.
|
||||
}]);
|
||||
```
|
||||
|
||||
To fix this problem, make sure you only define each module with the
|
||||
`angular.module(name, [requires])` syntax once across your entire project.
|
||||
Retrieve it for subsequent use with `angular.module(name)`. The fixed example
|
||||
is shown below.
|
||||
|
||||
```
|
||||
angular.module('myModule', [])
|
||||
.service('myCoolService', function () { /* ... */ });
|
||||
|
||||
angular.module('myModule')
|
||||
.directive('myDirective', ['myCoolService', function (myCoolService) {
|
||||
// This directive definition does not throw unknown provider.
|
||||
}]);
|
||||
```
|
||||
@@ -10,7 +10,7 @@ Angular's {@link ng.$sce Strict Contextual Escaping (SCE)} mode
|
||||
contexts to result in a value that is trusted as safe for use in such a context. (e.g. loading an
|
||||
Angular template from a URL requires that the URL is one considered safe for loading resources.)
|
||||
|
||||
This helps prevent XSS and other security issues. Read more at {@link
|
||||
api/ng.$sce Strict Contextual Escaping (SCE)}
|
||||
This helps prevent XSS and other security issues. Read more at
|
||||
{@link ng.$sce Strict Contextual Escaping (SCE)}
|
||||
|
||||
You may want to include the ngSanitize module to use the automatic sanitizing.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Using $location
|
||||
@sortOrder 500
|
||||
@description
|
||||
|
||||
# What does it do?
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Animations
|
||||
@sortOrder 310
|
||||
@description
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Bootstrap
|
||||
@sortOrder 350
|
||||
@description
|
||||
|
||||
# Bootstrap
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name HTML Compiler
|
||||
@sortOrder 330
|
||||
@description
|
||||
|
||||
<div class="alert alert-warning">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Conceptual Overview
|
||||
@sortOrder 200
|
||||
@description
|
||||
|
||||
# Conceptual Overview
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Controllers
|
||||
@sortOrder 220
|
||||
@description
|
||||
|
||||
# Understanding Controllers
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Working With CSS
|
||||
@sortOrder 510
|
||||
@description
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Data Binding
|
||||
@sortOrder 210
|
||||
@description
|
||||
|
||||
Data-binding in Angular apps is the automatic synchronization of data between the model and view
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Dependency Injection
|
||||
@sortOrder 250
|
||||
@description
|
||||
|
||||
# Dependency Injection
|
||||
@@ -109,7 +110,7 @@ asks the injector to create an instance of the controller and its dependencies.
|
||||
injector.instantiate(MyController);
|
||||
```
|
||||
|
||||
This is all done behinds the scenes. Notice that by having the `ng-controller` ask the injector to
|
||||
This is all done behind the scenes. Notice that by having the `ng-controller` ask the injector to
|
||||
instantiate the class, it can satisfy all of the dependencies of `MyController` without the
|
||||
controller ever knowing about the injector.
|
||||
|
||||
@@ -135,7 +136,7 @@ These can be used interchangeably as you see fit and are equivalent.
|
||||
|
||||
### Implicit Dependencies
|
||||
|
||||
The simplest way to get hold of the dependencies, is to assume that the function parameter names
|
||||
The simplest way to get hold of the dependencies is to assume that the function parameter names
|
||||
are the names of the dependencies.
|
||||
|
||||
```js
|
||||
@@ -144,7 +145,7 @@ function MyController($scope, greeter) {
|
||||
}
|
||||
```
|
||||
|
||||
Given a function the injector can infer the names of the service to inject by examining the
|
||||
Given a function the injector can infer the names of the services to inject by examining the
|
||||
function declaration and extracting the parameter names. In the above example `$scope`, and
|
||||
`greeter` are two services which need to be injected into the function.
|
||||
|
||||
@@ -154,7 +155,7 @@ rename the method parameter names. This makes this way of annotating only useful
|
||||
|
||||
### `$inject` Property Annotation
|
||||
|
||||
To allow the minifiers to rename the function parameters and still be able to inject right services,
|
||||
To allow the minifiers to rename the function parameters and still be able to inject the right services,
|
||||
the function needs to be annotated with the `$inject` property. The `$inject` property is an array
|
||||
of service names to inject.
|
||||
|
||||
@@ -166,7 +167,7 @@ MyController['$inject'] = ['$scope', 'greeter'];
|
||||
```
|
||||
|
||||
In this scenario the ordering of the values in the `$inject` array must match the ordering of the
|
||||
arguments to inject. Using above code snippet as an example, `$scope` will be injected into
|
||||
arguments to inject. Using the above code snippet as an example, `$scope` will be injected into
|
||||
`renamed$scope` and `greeter` into `renamedGreeter`. Care must be taken that the `$inject`
|
||||
annotation is kept in sync with the actual arguments in the function declaration.
|
||||
|
||||
@@ -206,7 +207,7 @@ someModule.factory('greeter', ['$window', function(renamed$window) {
|
||||
}]);
|
||||
```
|
||||
|
||||
Here, instead of simply providing the factory function, we pass an array, whose elements consist of
|
||||
Here, instead of simply providing the factory function, we pass an array whose elements consist of
|
||||
a list of strings (the names of the dependencies) followed by the function itself.
|
||||
|
||||
Keep in mind that all of the annotation styles are equivalent and can be used anywhere in Angular
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Directives
|
||||
@sortOrder 300
|
||||
@description
|
||||
|
||||
# Creating Custom Directives
|
||||
@@ -909,6 +910,30 @@ Looking back at `myPane`'s definition, notice the last argument in its `link` fu
|
||||
When a directive requires a controller, it receives that controller as the fourth argument of its
|
||||
`link` function. Taking advantage of this, `myPane` can call the `addPane` function of `myTabs`.
|
||||
|
||||
If multiple controllers are required, the `require` option of the directive can take an array argument.
|
||||
The corresponding parameter being sent to the `link` function will also be an array.
|
||||
|
||||
```js
|
||||
angular.module('docsTabsExample', [])
|
||||
.directive('myPane', function() {
|
||||
return {
|
||||
require: ['^myTabs', '^ngModel'],
|
||||
restrict: 'E',
|
||||
transclude: true,
|
||||
scope: {
|
||||
title: '@'
|
||||
},
|
||||
link: function(scope, element, attrs, controllers) {
|
||||
var tabsCtrl = controllers[0],
|
||||
modelCtrl = controllers[1];
|
||||
|
||||
tabsCtrl.addPane(scope);
|
||||
},
|
||||
templateUrl: 'my-pane.html'
|
||||
};
|
||||
});
|
||||
```
|
||||
|
||||
Savvy readers may be wondering what the difference is between `link` and `controller`.
|
||||
The basic difference is that `controller` can expose an API, and `link` functions can interact with
|
||||
controllers using `require`.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@workInProgress
|
||||
@ngdoc overview
|
||||
@name E2E Testing
|
||||
@sortOrder 420
|
||||
@description
|
||||
|
||||
# E2E Testing
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Expressions
|
||||
@sortOrder 270
|
||||
@description
|
||||
|
||||
# Angular Expressions
|
||||
@@ -38,7 +39,9 @@ the method from your view. If you want to `eval()` an Angular expression yoursel
|
||||
## Example
|
||||
<example>
|
||||
<file name="index.html">
|
||||
1+2={{1+2}}
|
||||
<span>
|
||||
1+2={{1+2}}
|
||||
</span>
|
||||
</file>
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Filters
|
||||
@sortOrder 280
|
||||
@description
|
||||
|
||||
A filter formats the value of an expression for display to the user. They can be used in view templates,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Forms
|
||||
@sortOrder 290
|
||||
@description
|
||||
|
||||
Controls (`input`, `select`, `textarea`) are ways for a user to enter data.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name i18n and l10n
|
||||
@sortOrder 520
|
||||
@description
|
||||
|
||||
# i18n and l10n
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Internet Explorer Compatibility
|
||||
@sortOrder 530
|
||||
@description
|
||||
|
||||
# Internet Explorer Compatibility
|
||||
|
||||
@@ -47,7 +47,7 @@ In Angular applications, you move the job of filling page templates with data fr
|
||||
|
||||
### Testing
|
||||
|
||||
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/dev_guide.unit-testing Unit testing}, {@link guide/dev_guide.services.testing_services Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
|
||||
* **Unit testing:** [Using Karma (video)](http://www.youtube.com/watch?v=YG5DEzaQBIc), {@link guide/unit-testing Unit testing}, {@link guide/services#unit-testing Testing services}, [Karma in Webstorm](http://blog.jetbrains.com/webstorm/2013/10/running-javascript-tests-with-karma-in-webstorm-7/)
|
||||
* **Scenario testing:** [Protractor](https://github.com/angular/protractor)
|
||||
|
||||
## Specific Topics
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Introduction
|
||||
@sortOrder 100
|
||||
@description
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Migrating from 1.0 to 1.2
|
||||
@sortOrder 550
|
||||
@description
|
||||
|
||||
# Migrating from 1.0 to 1.2
|
||||
@@ -382,8 +383,6 @@ See [80739409](https://github.com/angular/angular.js/commit/807394095b991357225a
|
||||
|
||||
## ngBindHtmlUnsafe has been removed and replaced by ngBindHtml
|
||||
|
||||
`ngBindHtml` which has been moved from `ngSanitize` module to the core `ng` module.
|
||||
|
||||
`ngBindHtml` provides `ngBindHtmlUnsafe` like
|
||||
behavior (evaluate an expression and innerHTML the result into the DOM) when bound to the result
|
||||
of `$sce.trustAsHtml(string)`. When bound to a plain string, the string is sanitized via
|
||||
@@ -391,6 +390,10 @@ of `$sce.trustAsHtml(string)`. When bound to a plain string, the string is sanit
|
||||
module is not loaded) and the bound expression evaluates to a value that is not trusted an
|
||||
exception is thrown.
|
||||
|
||||
When using this directive you can either include `ngSanitize` in your module's dependencis (See the
|
||||
example at the {@link ngBindHtml} reference) or use the {@link $sce} service to set the value as
|
||||
trusted.
|
||||
|
||||
See [dae69473](https://github.com/angular/angular.js/commit/dae694739b9581bea5dbc53522ec00d87b26ae55).
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Modules
|
||||
@sortOrder 320
|
||||
@description
|
||||
|
||||
# What is a Module?
|
||||
@@ -26,10 +27,12 @@ should be bootstrapped. There are several advantages to this approach:
|
||||
|
||||
I'm in a hurry. How do I get a Hello World module working?
|
||||
|
||||
<example module='myApp'>
|
||||
<example ng-app-included="true">
|
||||
<file name="index.html">
|
||||
<div>
|
||||
{{ 'World' | greet }}
|
||||
<div ng-app="myApp">
|
||||
<div>
|
||||
{{ 'World' | greet }}
|
||||
</div>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
@@ -56,7 +59,7 @@ I'm in a hurry. How do I get a Hello World module working?
|
||||
Important things to notice:
|
||||
|
||||
* The {@link angular.Module Module} API
|
||||
* The reference to `myApp` module in `<html ng-app="myApp">`.
|
||||
* The reference to `myApp` module in `<div ng-app="myApp">`.
|
||||
This is what bootstraps the app using your module.
|
||||
* The empty array in `angular.module('myApp', [])`.
|
||||
This array is the list of modules `myApp` depends on.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Providers
|
||||
@sortOrder 340
|
||||
@description
|
||||
|
||||
# Providers
|
||||
@@ -97,9 +98,8 @@ created by this recipe.
|
||||
Note: All services in Angular are singletons. That means that the injector uses each recipe at most
|
||||
once to create the object. The injector then caches the reference for all future needs.
|
||||
|
||||
Since Factory is more a powerful version of the Value recipe, you can construct the same service with it.
|
||||
Using our previous `clientId` Value recipe example, we can rewrite it as a Factory recipe like
|
||||
this:
|
||||
Since a Factory is a more powerful version of the Value recipe, the same service can be constructed with it.
|
||||
Using our previous `clientId` Value recipe example, we can rewrite it as a Factory recipe like this:
|
||||
|
||||
```javascript
|
||||
myApp.factory('clientId', function clientIdFactory() {
|
||||
@@ -134,11 +134,11 @@ token.
|
||||
|
||||
<div class="alert alert-success">
|
||||
**Best Practice:** name the factory functions as `<serviceId>Factory`
|
||||
(e.g. apiTokenFactory). While this naming convention is not required, it helps when navigating the code base
|
||||
(e.g., apiTokenFactory). While this naming convention is not required, it helps when navigating the codebase
|
||||
or looking at stack traces in the debugger.
|
||||
</div>
|
||||
|
||||
Just like with Value recipe, Factory recipe can create a service of any type, whether it be a
|
||||
Just like with the Value recipe, the Factory recipe can create a service of any type, whether it be a
|
||||
primitive, object literal, function, or even an instance of a custom type.
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ function UnicornLauncher(apiToken) {
|
||||
|
||||
this.launchedCount = 0;
|
||||
this.launch = function() {
|
||||
// make a request to the remote api and include the apiToken
|
||||
// Make a request to the remote API and include the apiToken
|
||||
...
|
||||
this.launchedCount++;
|
||||
}
|
||||
@@ -170,7 +170,7 @@ myApp.factory('unicornLauncher', ["apiToken", function(apiToken) {
|
||||
```
|
||||
|
||||
|
||||
This is, however, exactly the use-case that Service recipe is the most suitable for.
|
||||
This is, however, exactly the use-case that the Service recipe is the most suitable for.
|
||||
|
||||
The Service recipe produces a service just like the Value or Factory recipes, but it does so by
|
||||
*invoking a constructor with the `new` operator*. The constructor can take zero or more arguments,
|
||||
@@ -189,7 +189,7 @@ myApp.service('unicornLauncher', ["apiToken", UnicornLauncher]);
|
||||
Much simpler!
|
||||
|
||||
Note: Yes, we have called one of our service recipes 'Service'. We regret this and know that we'll
|
||||
be somehow punished for our mis-deed. It's like we named one of our offspring 'Child'. Boy,
|
||||
be somehow punished for our misdeed. It's like we named one of our offspring 'Child'. Boy,
|
||||
that would mess with the teachers.
|
||||
|
||||
|
||||
@@ -199,8 +199,8 @@ As already mentioned in the intro, the Provider recipe is the core recipe type a
|
||||
all the other recipe types are just syntactic sugar on top of it. It is the most verbose recipe
|
||||
with the most abilities, but for most services it's overkill.
|
||||
|
||||
Provider recipe is syntactically defined as a custom type that implements a `$get` method. This
|
||||
method is a factory function just like the one we use in Factory recipe. In fact, if you define
|
||||
The Provider recipe is syntactically defined as a custom type that implements a `$get` method. This
|
||||
method is a factory function just like the one we use in the Factory recipe. In fact, if you define
|
||||
a Factory recipe, an empty Provider type with the `$get` method set to your factory function is
|
||||
automatically created under the hood.
|
||||
|
||||
@@ -248,7 +248,7 @@ and wires (injects) all provider instances only.
|
||||
|
||||
During application bootstrap, before Angular goes off creating all services, it configures and
|
||||
instantiates all providers. We call this the configuration phase of the application life-cycle.
|
||||
During this phase services aren't accessible because they haven't been created yet.
|
||||
During this phase, services aren't accessible because they haven't been created yet.
|
||||
|
||||
Once the configuration phase is over, interaction with providers is disallowed and the process of
|
||||
creating services starts. We call this part of the application life-cycle the run phase.
|
||||
@@ -259,9 +259,9 @@ creating services starts. We call this part of the application life-cycle the ru
|
||||
We've just learned how Angular splits the life-cycle into configuration phase and run phase and how
|
||||
you can provide configuration to your application via the config function. Since the config
|
||||
function runs in the configuration phase when no services are available, it doesn't have access
|
||||
even to simple value objects created via Value recipe.
|
||||
even to simple value objects created via the Value recipe.
|
||||
|
||||
Since simple values, like url prefix, don't have dependencies or configuration, it is often handy
|
||||
Since simple values, like URL prefixes, don't have dependencies or configuration, it's often handy
|
||||
to make them available in both the configuration and run phases. This is what the Constant recipe
|
||||
is for.
|
||||
|
||||
@@ -317,7 +317,7 @@ Let's take a look at how we would create a very simple component via the directi
|
||||
on the `planetName` constant we've just defined and displays the planet name, in our case:
|
||||
"Planet Name: Greasy Giant".
|
||||
|
||||
Since the directives are registered via Factory recipe, we can use the same syntax as with factories.
|
||||
Since the directives are registered via the Factory recipe, we can use the same syntax as with factories.
|
||||
|
||||
```javascript
|
||||
myApp.directive('myPlanet', ['planetName', function myPlanetDirectiveFactory(planetName) {
|
||||
@@ -340,7 +340,7 @@ We can then use the component like this:
|
||||
</html>
|
||||
```
|
||||
|
||||
Using Factory recipes you can also define Angular's filters and animations, but the controllers
|
||||
Using Factory recipes, you can also define Angular's filters and animations, but the controllers
|
||||
are a bit special. You create a controller as a custom type that declares its dependencies as
|
||||
arguments for its constructor function. This constructor is then registered with a module. Let's
|
||||
take a look at the `DemoController`, created in one of the early examples:
|
||||
@@ -351,7 +351,7 @@ myApp.controller('DemoController', ['clientId', function DemoController(clientId
|
||||
}]);
|
||||
```
|
||||
|
||||
The DemoController is instantiated via its constructor every time the app needs an instance of
|
||||
The DemoController is instantiated via its constructor, every time the app needs an instance of
|
||||
DemoController (in our simple app it's just once). So unlike services, controllers are not
|
||||
singletons. The constructor is called with all the requested services, in our case the `clientId`
|
||||
service.
|
||||
@@ -365,12 +365,12 @@ To wrap it up, let's summarize the most important points:
|
||||
- There are five recipe types that define how to create objects: Value, Factory, Service, Provider
|
||||
and Constant.
|
||||
- Factory and Service are the most commonly used recipes. The only difference between them is that
|
||||
Service recipe works better for objects of custom type, while Factory can produce JavaScript
|
||||
the Service recipe works better for objects of a custom type, while the Factory can produce JavaScript
|
||||
primitives and functions.
|
||||
- The Provider recipe is the core recipe type and all the other ones are just syntactic sugar on it.
|
||||
- Provider is the most complex recipe type. You don't need it unless you are building a reusable
|
||||
piece of code that needs global configuration.
|
||||
- All special purpose objects except for Controller are defined via Factory recipes.
|
||||
- All special purpose objects except for the Controller are defined via Factory recipes.
|
||||
|
||||
<table class="table table-bordered code-table">
|
||||
<thead>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Scopes
|
||||
@sortOrder 240
|
||||
@description
|
||||
|
||||
# What are Scopes?
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Services
|
||||
@sortOrder 230
|
||||
@description
|
||||
|
||||
# Services
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Templates
|
||||
@sortOrder 260
|
||||
@description
|
||||
|
||||
In Angular, templates are written with HTML that contains Angular-specific elements and attributes.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
@ngdoc overview
|
||||
@name Unit Testing
|
||||
@sortOrder 410
|
||||
@description
|
||||
|
||||
JavaScript is a dynamically typed language which comes with great power of expression, but it also
|
||||
|
||||
@@ -62,13 +62,13 @@ minified AngularJS files:
|
||||
|
||||
```shell
|
||||
# Clone your Github repository:
|
||||
git clone git@github.com:<github username>/angular.js.git
|
||||
git clone "git@github.com:<github username>/angular.js.git"
|
||||
|
||||
# Go to the AngularJS directory:
|
||||
cd angular.js
|
||||
|
||||
# Add the main AngularJS repository as an upstream remote to your repository:
|
||||
git remote add upstream https://github.com/angular/angular.js.git
|
||||
git remote add upstream "https://github.com/angular/angular.js.git"
|
||||
|
||||
# Install node.js dependencies:
|
||||
npm install
|
||||
@@ -126,13 +126,13 @@ made available a local web server based on Node.js.
|
||||
```
|
||||
|
||||
2. To access the local server, enter the following URL into your web browser:
|
||||
```
|
||||
```text
|
||||
http://localhost:8000/
|
||||
```
|
||||
By default, it serves the contents of the AngularJS project directory.
|
||||
|
||||
3. To access the locally served docs, visit this URL:
|
||||
```
|
||||
```text
|
||||
http://localhost:8000/build/docs/
|
||||
```
|
||||
|
||||
@@ -165,7 +165,7 @@ change. To execute tests in this mode run:
|
||||
2. To capture more browsers, open this URL in the desired browser (URL might be different if you have multiple instance
|
||||
of Karma running, read Karma's console output for the correct URL):
|
||||
|
||||
```shell
|
||||
```text
|
||||
http://localhost:9876/
|
||||
```
|
||||
|
||||
|
||||
@@ -44,8 +44,8 @@ really digging into it. If you're looking for a shorter introduction to AngularJ
|
||||
|
||||
# Get Started
|
||||
|
||||
The rest of this page explains how you can set up your machine to work with the code on your local
|
||||
machine. If you just want to read the tutorial then you can just go straight to the first step:
|
||||
The rest of this page explains how you can set up your local machine for development.
|
||||
If you just want to read the tutorial then you can just go straight to the first step:
|
||||
[Step 0 - Bootstrapping](tutorial/step_00).
|
||||
|
||||
# Working with the code
|
||||
|
||||
@@ -59,7 +59,7 @@ tag as the template.
|
||||
by the value of the expressions.
|
||||
|
||||
We have added a new directive, called `ng-controller`, which attaches a `PhoneListCtrl`
|
||||
__controller__ to the DOM at this point:
|
||||
__controller__ to the <body> tag. At this point:
|
||||
|
||||
* The expressions in curly braces (`{{phone.name}}` and `{{phone.snippet}}` denote
|
||||
bindings, which are referring to our application model, which is set up in our `PhoneListCtrl`
|
||||
@@ -210,6 +210,11 @@ To run the tests, and then watch the files for changes: `npm test`.
|
||||
* To rerun the tests, just change any of the source or test .js files. Karma will notice the change
|
||||
and will rerun the tests for you. Now isn't that sweet?
|
||||
|
||||
<div class="alert alert-info">
|
||||
Make sure you don't minimize the browser that Karma opened. On some OS, memory assigned to a minimized
|
||||
browser is limited, which results in your karma tests running extremely slow.
|
||||
</div>
|
||||
|
||||
# Experiments
|
||||
|
||||
* Add another binding to `index.html`. For example:
|
||||
@@ -245,6 +250,8 @@ To run the tests, and then watch the files for changes: `npm test`.
|
||||
<tr><th>row number</th></tr>
|
||||
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
|
||||
</table>
|
||||
|
||||
Extra points: try and make an 8x8 table using an additional ng-repeat.
|
||||
|
||||
* Make the unit test fail by changing `expect(scope.phones.length).toBe(3)` to instead use `toBe(4)`.
|
||||
|
||||
|
||||
@@ -145,16 +145,39 @@ Display the current value of the `query` model by adding a `{{query}}` binding i
|
||||
### Display Query in Title
|
||||
Let's see how we can get the current value of the `query` model to appear in the HTML page title.
|
||||
|
||||
* Add the following end-to-end test into the `describe` block within `test/e2e/scenarios.js`:
|
||||
* Add an end-to-end test into the `describe` block, `test/e2e/scenarios.js` should look like this:
|
||||
|
||||
```js
|
||||
it('should display the current filter value in the title bar', function() {
|
||||
describe('PhoneCat App', function() {
|
||||
|
||||
expect(browser.getTitle()).toMatch(/Google Phone Gallery:\s*$/);
|
||||
|
||||
element(by.model('query')).sendKeys('nexus');
|
||||
|
||||
expect(browser.getTitle()).toMatch(/Google Phone Gallery: nexus$/);
|
||||
describe('Phone list view', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
browser.get('app/index.html');
|
||||
});
|
||||
|
||||
var phoneList = element.all(by.repeater('phone in phones'));
|
||||
var query = element(by.model('query'));
|
||||
|
||||
it('should filter the phone list as user types into the search box', function() {
|
||||
expect(phoneList.count()).toBe(3);
|
||||
|
||||
query.sendKeys('nexus');
|
||||
expect(phoneList.count()).toBe(1);
|
||||
|
||||
query.clear();
|
||||
query.sendKeys('motorola');
|
||||
expect(phoneList.count()).toBe(2);
|
||||
});
|
||||
|
||||
it('should display the current filter value in the title bar', function() {
|
||||
query.clear();
|
||||
expect(browser.getTitle()).toMatch(/Google Phone Gallery:\s*$/);
|
||||
|
||||
query.sendKeys('nexus');
|
||||
expect(browser.getTitle()).toMatch(/Google Phone Gallery: nexus$/);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ Because we started using dependency injection and our controller has dependencie
|
||||
controller in our tests is a bit more complicated. We could use the `new` operator and provide the
|
||||
constructor with some kind of fake `$http` implementation. However, Angular provides a mock `$http`
|
||||
service that we can use in unit tests. We configure "fake" responses to server requests by calling
|
||||
methods on a service called $httpBackend:
|
||||
methods on a service called `$httpBackend`:
|
||||
|
||||
```js
|
||||
describe('PhoneCat controllers', function() {
|
||||
|
||||
@@ -202,7 +202,7 @@ moved the controllers into their own module `phonecatControllers` (as shown belo
|
||||
|
||||
We added `angular-route.js` to `index.html` and created a new `phonecatControllers` module in
|
||||
`controllers.js`. That's not all we need to do to be able to use their code, however. We also have
|
||||
to add the modules dependencies of our app. By listing these two modules as dependencies of
|
||||
to add the modules as dependencies of our app. By listing these two modules as dependencies of
|
||||
`phonecatApp`, we can use the directives and services they provide.
|
||||
|
||||
|
||||
|
||||
@@ -102,6 +102,50 @@ __`test/e2e/scenarios.js`:__
|
||||
You can now rerun `npm run protractor` to see the tests run.
|
||||
|
||||
|
||||
You also have to refactor one of your unit tests because of the addition of the `mainImageUrl`
|
||||
model property to the `PhoneDetailCtrl` controller. Below, we create the function `xyzPhoneData`
|
||||
which returns the appropriate json with the `images` attribute in order to get the test to pass.
|
||||
|
||||
__`test/unit/controllersSpec.js`:__
|
||||
|
||||
```js
|
||||
...
|
||||
beforeEach(module('phonecatApp'));
|
||||
|
||||
...
|
||||
|
||||
describe('PhoneDetailCtrl', function(){
|
||||
var scope, $httpBackend, ctrl,
|
||||
xyzPhoneData = function() {
|
||||
return {
|
||||
name: 'phone xyz',
|
||||
images: ['image/url1.png', 'image/url2.png']
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
beforeEach(inject(function(_$httpBackend_, $rootScope, $routeParams, $controller) {
|
||||
$httpBackend = _$httpBackend_;
|
||||
$httpBackend.expectGET('phones/xyz.json').respond(xyzPhoneData());
|
||||
|
||||
$routeParams.phoneId = 'xyz';
|
||||
scope = $rootScope.$new();
|
||||
ctrl = $controller('PhoneDetailCtrl', {$scope: scope});
|
||||
}));
|
||||
|
||||
|
||||
it('should fetch phone detail', function() {
|
||||
expect(scope.phone).toBeUndefined();
|
||||
$httpBackend.flush();
|
||||
|
||||
expect(scope.phone).toEqual(xyzPhoneData());
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
Your unit tests should now be passing.
|
||||
|
||||
|
||||
# Experiments
|
||||
|
||||
* Let's add a new controller method to `PhoneDetailCtrl`:
|
||||
|
||||
+5
-2
@@ -1,3 +1,5 @@
|
||||
"use strict";
|
||||
|
||||
var path = require('canonical-path');
|
||||
var versionInfo = require('../lib/versions/version-info');
|
||||
var basePath = __dirname;
|
||||
@@ -9,9 +11,10 @@ module.exports = function(config) {
|
||||
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + versionInfo.cdnVersion;
|
||||
|
||||
var getVersion = function(component, sourceFolder, packageFile) {
|
||||
sourceFolder = sourceFolder || '../bower_components';
|
||||
sourceFolder = sourceFolder || './bower_components';
|
||||
packageFile = packageFile || 'bower.json';
|
||||
return require(path.join(sourceFolder,component,packageFile)).version;
|
||||
|
||||
return require(path.resolve(sourceFolder,component,packageFile)).version;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+13
-3
@@ -1,4 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
var gulp = require('gulp');
|
||||
var log = require('gulp-util').log;
|
||||
var concat = require('gulp-concat');
|
||||
var jshint = require('gulp-jshint');
|
||||
var bower = require('bower');
|
||||
@@ -12,7 +15,7 @@ var path = require('canonical-path');
|
||||
// See clean and bower for async tasks, and see assets and doc-gen for dependent tasks below
|
||||
|
||||
var outputFolder = '../build/docs';
|
||||
var bowerFolder = '../bower_components';
|
||||
var bowerFolder = 'bower_components';
|
||||
|
||||
|
||||
var copyComponent = function(component, pattern, sourceFolder, packageFile) {
|
||||
@@ -26,7 +29,14 @@ var copyComponent = function(component, pattern, sourceFolder, packageFile) {
|
||||
};
|
||||
|
||||
gulp.task('bower', function() {
|
||||
return bower.commands.install();
|
||||
var bowerTask = bower.commands.install();
|
||||
bowerTask.on('log', function (result) {
|
||||
log('bower:', result.id, result.data.endpoint.name);
|
||||
});
|
||||
bowerTask.on('error', function(error) {
|
||||
log(error);
|
||||
});
|
||||
return bowerTask;
|
||||
});
|
||||
|
||||
gulp.task('build-app', function() {
|
||||
@@ -48,7 +58,7 @@ gulp.task('assets', ['bower'], function() {
|
||||
});
|
||||
|
||||
|
||||
gulp.task('doc-gen', function() {
|
||||
gulp.task('doc-gen', ['bower'], function() {
|
||||
var generateDocs = dgeni.generator('docs.config.js');
|
||||
return generateDocs()
|
||||
.catch(function(error) {
|
||||
|
||||
@@ -12,9 +12,9 @@ set -e
|
||||
# before_script:
|
||||
# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash
|
||||
|
||||
CONNECT_URL="http://saucelabs.com/downloads/Sauce-Connect-latest.zip"
|
||||
CONNECT_URL="https://d2nkw87yt5k0to.cloudfront.net/downloads/sc-4.3-linux.tar.gz"
|
||||
CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
|
||||
CONNECT_DOWNLOAD="Sauce_Connect.zip"
|
||||
CONNECT_DOWNLOAD="sc-4.3-linux.tar.gz"
|
||||
|
||||
CONNECT_LOG="$LOGS_DIR/sauce-connect"
|
||||
CONNECT_STDOUT="$LOGS_DIR/sauce-connect.stdout"
|
||||
@@ -24,7 +24,8 @@ CONNECT_STDERR="$LOGS_DIR/sauce-connect.stderr"
|
||||
mkdir -p $CONNECT_DIR
|
||||
cd $CONNECT_DIR
|
||||
curl $CONNECT_URL -o $CONNECT_DOWNLOAD 2> /dev/null 1> /dev/null
|
||||
unzip $CONNECT_DOWNLOAD > /dev/null
|
||||
mkdir sauce-connect
|
||||
tar --extract --file=$CONNECT_DOWNLOAD --strip-components=1 --directory=sauce-connect > /dev/null
|
||||
rm $CONNECT_DOWNLOAD
|
||||
|
||||
SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||
@@ -45,5 +46,5 @@ echo "Starting Sauce Connect in the background, logging into:"
|
||||
echo " $CONNECT_LOG"
|
||||
echo " $CONNECT_STDOUT"
|
||||
echo " $CONNECT_STDERR"
|
||||
java -jar Sauce-Connect.jar $ARGS $SAUCE_USERNAME $SAUCE_ACCESS_KEY \
|
||||
sauce-connect/bin/sc -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY $ARGS -v \
|
||||
--logfile $CONNECT_LOG 2> $CONNECT_STDERR 1> $CONNECT_STDOUT &
|
||||
|
||||
Generated
+1770
-604
File diff suppressed because it is too large
Load Diff
+31
-25
@@ -6,11 +6,20 @@
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-benchpress": "0.x.x",
|
||||
"benchmark": "1.x.x",
|
||||
"bower": "~1.3.9",
|
||||
"browserstacktunnel-wrapper": "~1.1.1",
|
||||
"canonical-path": "0.0.2",
|
||||
"dgeni": "^0.3.0",
|
||||
"dgeni-packages": "^0.9.8",
|
||||
"es6-shim": "^0.14.0",
|
||||
"event-stream": "~3.1.0",
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-bump": "~0.0.13",
|
||||
"grunt-contrib-clean": "~0.5.0",
|
||||
"grunt-contrib-connect": "~0.5.0",
|
||||
"grunt-contrib-compress": "~0.5.2",
|
||||
"grunt-contrib-connect": "~0.5.0",
|
||||
"grunt-contrib-copy": "~0.4.1",
|
||||
"grunt-contrib-jshint": "~0.10.0",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
@@ -19,41 +28,38 @@
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"grunt-parallel": "~0.3.1",
|
||||
"grunt-shell": "~0.4.0",
|
||||
"load-grunt-tasks": "~0.3.0",
|
||||
"bower": "~1.2.2",
|
||||
"gulp": "~3.8.0",
|
||||
"gulp-concat": "~2.1.7",
|
||||
"gulp-jshint": "~1.4.2",
|
||||
"gulp-util": "^3.0.1",
|
||||
"jasmine-node": "~1.11.0",
|
||||
"q": "~1.0.0",
|
||||
"shelljs": "~0.2.6",
|
||||
"jasmine-reporters": "~0.2.1",
|
||||
"jshint-stylish": "~0.1.5",
|
||||
"karma": "^0.12.0",
|
||||
"karma-jasmine": "0.1.5",
|
||||
"karma-browserstack-launcher": "0.0.7",
|
||||
"karma-chrome-launcher": "0.1.2",
|
||||
"karma-firefox-launcher": "0.1.3",
|
||||
"karma-ng-scenario": "0.1.0",
|
||||
"karma-jasmine": "0.1.5",
|
||||
"karma-junit-reporter": "0.2.1",
|
||||
"karma-ng-scenario": "0.1.0",
|
||||
"karma-sauce-launcher": "0.2.0",
|
||||
"karma-script-launcher": "0.1.0",
|
||||
"karma-browserstack-launcher": "0.0.7",
|
||||
"protractor": "1.0.0",
|
||||
"yaml-js": "~0.0.8",
|
||||
"rewire": "1.1.3",
|
||||
"promises-aplus-tests": "~1.3.2",
|
||||
"semver": "~2.1.0",
|
||||
"load-grunt-tasks": "~0.3.0",
|
||||
"lodash": "~2.1.0",
|
||||
"browserstacktunnel-wrapper": "~1.1.1",
|
||||
"jasmine-reporters": "~0.2.1",
|
||||
"gulp": "~3.4.0",
|
||||
"event-stream": "~3.1.0",
|
||||
"marked": "~0.3.0",
|
||||
"gulp-concat": "~2.1.7",
|
||||
"canonical-path": "0.0.2",
|
||||
"winston": "~0.7.2",
|
||||
"dgeni": "^0.3.0",
|
||||
"dgeni-packages": "^0.9.6",
|
||||
"gulp-jshint": "~1.4.2",
|
||||
"jshint-stylish": "~0.1.5",
|
||||
"node-html-encoder": "0.0.2",
|
||||
"promises-aplus-tests": "~1.3.2",
|
||||
"protractor": "1.2.0",
|
||||
"q": "~1.0.0",
|
||||
"q-io": "^1.10.9",
|
||||
"qq": "^0.3.5",
|
||||
"rewire": "1.1.3",
|
||||
"semver": "~2.1.0",
|
||||
"shelljs": "~0.2.6",
|
||||
"sorted-object": "^1.0.0",
|
||||
"qq": "^0.3.5"
|
||||
"stringmap": "^0.2.2",
|
||||
"winston": "~0.7.2",
|
||||
"yaml-js": "~0.0.8"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ var config = require('./protractor-shared-conf').config;
|
||||
|
||||
config.specs = [
|
||||
'build/docs/ptore2e/**/*.js',
|
||||
'test/e2e/docsAppE2E.js'
|
||||
'docs/app/e2e/docsAppE2E.js'
|
||||
];
|
||||
|
||||
config.capabilities = {
|
||||
|
||||
@@ -5,7 +5,7 @@ exports.config = {
|
||||
|
||||
specs: [
|
||||
'build/docs/ptore2e/**/*.js',
|
||||
'test/e2e/docsAppE2E.js'
|
||||
'docs/app/e2e/docsAppE2E.js'
|
||||
],
|
||||
|
||||
capabilities: {
|
||||
|
||||
@@ -7,9 +7,11 @@ config.sauceKey = process.env.SAUCE_ACCESS_KEY;
|
||||
|
||||
config.multiCapabilities = [{
|
||||
'browserName': 'chrome',
|
||||
'platform': 'OS X 10.9',
|
||||
'name': 'Angular E2E',
|
||||
'tunnel-identifier': process.env.TRAVIS_JOB_NUMBER,
|
||||
'build': process.env.TRAVIS_BUILD_NUMBER
|
||||
'build': process.env.TRAVIS_BUILD_NUMBER,
|
||||
'version': '34'
|
||||
}, {
|
||||
'browserName': 'firefox',
|
||||
'name': 'Angular E2E',
|
||||
|
||||
@@ -23,9 +23,6 @@ function cleanModule(module, name) {
|
||||
if (name === 'chokidar') {
|
||||
if (module.version === '0.8.1') {
|
||||
delete module.dependencies;
|
||||
} else if ( module.version !== '0.8.2') {
|
||||
throw new Error("Unfamiliar chokidar version (v" + module.version +
|
||||
") , please check status of https://github.com/paulmillr/chokidar/pull/106");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ elif [ $JOB = "e2e" ]; then
|
||||
if [ $TEST_TARGET = "jquery" ]; then
|
||||
TARGET_SPECS="build/docs/ptore2e/**/*jquery_test.js"
|
||||
elif [ $TEST_TARGET = "doce2e" ]; then
|
||||
TARGET_SPECS="test/e2e/docsAppE2E.js"
|
||||
TARGET_SPECS="docs/app/e2e/docsAppE2E.js"
|
||||
fi
|
||||
grunt test:travis-protractor --specs "$TARGET_SPECS"
|
||||
else
|
||||
|
||||
@@ -6,4 +6,4 @@ set -e
|
||||
cd $(dirname $0);
|
||||
|
||||
cd ../..
|
||||
curl "http://23.251.148.50:8000/tar/$TRAVIS_REPO_SLUG/$TRAVIS_COMMIT" | tar xz
|
||||
curl "http://23.251.148.50:8000/tar/$TRAVIS_REPO_SLUG/$TRAVIS_COMMIT" | tar xz || true
|
||||
+14
-5
@@ -820,9 +820,13 @@ function copy(source, destination, stackSource, stackDest) {
|
||||
}
|
||||
} else {
|
||||
var h = destination.$$hashKey;
|
||||
forEach(destination, function(value, key) {
|
||||
delete destination[key];
|
||||
});
|
||||
if (isArray(destination)) {
|
||||
destination.length = 0;
|
||||
} else {
|
||||
forEach(destination, function(value, key) {
|
||||
delete destination[key];
|
||||
});
|
||||
}
|
||||
for ( var key in source) {
|
||||
result = copy(source[key], null, stackSource, stackDest);
|
||||
if (isObject(source[key])) {
|
||||
@@ -907,7 +911,8 @@ function equals(o1, o2) {
|
||||
return true;
|
||||
}
|
||||
} else if (isDate(o1)) {
|
||||
return isDate(o2) && o1.getTime() == o2.getTime();
|
||||
if (!isDate(o2)) return false;
|
||||
return (isNaN(o1.getTime()) && isNaN(o2.getTime())) || (o1.getTime() === o2.getTime());
|
||||
} else if (isRegExp(o1) && isRegExp(o2)) {
|
||||
return o1.toString() == o2.toString();
|
||||
} else {
|
||||
@@ -1345,7 +1350,11 @@ function bootstrap(element, modules) {
|
||||
|
||||
if (element.injector()) {
|
||||
var tag = (element[0] === document) ? 'document' : startingTag(element);
|
||||
throw ngMinErr('btstrpd', "App Already Bootstrapped with this Element '{0}'", tag);
|
||||
//Encode angle brackets to prevent input from being sanitized to empty string #8683
|
||||
throw ngMinErr(
|
||||
'btstrpd',
|
||||
"App Already Bootstrapped with this Element '{0}'",
|
||||
tag.replace(/</,'<').replace(/>/,'>'));
|
||||
}
|
||||
|
||||
modules = modules || [];
|
||||
|
||||
@@ -109,11 +109,11 @@ function publishExternalAPI(angular){
|
||||
'element': jqLite,
|
||||
'forEach': forEach,
|
||||
'injector': createInjector,
|
||||
'noop':noop,
|
||||
'bind':bind,
|
||||
'noop': noop,
|
||||
'bind': bind,
|
||||
'toJson': toJson,
|
||||
'fromJson': fromJson,
|
||||
'identity':identity,
|
||||
'identity': identity,
|
||||
'isUndefined': isUndefined,
|
||||
'isDefined': isDefined,
|
||||
'isString': isString,
|
||||
|
||||
+28
-12
@@ -952,21 +952,37 @@ forEach({
|
||||
|
||||
clone: jqLiteClone,
|
||||
|
||||
triggerHandler: function(element, eventName, eventData) {
|
||||
// Copy event handlers in case event handlers array is modified during execution.
|
||||
var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName],
|
||||
eventFnsCopy = shallowCopy(eventFns || []);
|
||||
triggerHandler: function(element, event, extraParameters) {
|
||||
|
||||
eventData = eventData || [];
|
||||
var dummyEvent, eventFnsCopy, handlerArgs;
|
||||
var eventName = event.type || event;
|
||||
var eventFns = (jqLiteExpandoStore(element, 'events') || {})[eventName];
|
||||
|
||||
var event = [{
|
||||
preventDefault: noop,
|
||||
stopPropagation: noop
|
||||
}];
|
||||
if (eventFns) {
|
||||
|
||||
forEach(eventFnsCopy, function(fn) {
|
||||
fn.apply(element, event.concat(eventData));
|
||||
});
|
||||
// Create a dummy event to pass to the handlers
|
||||
dummyEvent = {
|
||||
preventDefault: function() { this.defaultPrevented = true; },
|
||||
isDefaultPrevented: function() { return this.defaultPrevented === true; },
|
||||
stopPropagation: noop,
|
||||
type: eventName,
|
||||
target: element
|
||||
};
|
||||
|
||||
// If a custom event was provided then extend our dummy event with it
|
||||
if (event.type) {
|
||||
dummyEvent = extend(dummyEvent, event);
|
||||
}
|
||||
|
||||
// Copy event handlers in case event handlers array is modified during execution.
|
||||
eventFnsCopy = shallowCopy(eventFns);
|
||||
handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
|
||||
|
||||
forEach(eventFnsCopy, function(fn) {
|
||||
fn.apply(element, handlerArgs);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}, function(fn, name){
|
||||
/**
|
||||
|
||||
+3
-2
@@ -114,7 +114,7 @@ function setupModuleLoader(window) {
|
||||
* @ngdoc property
|
||||
* @name angular.Module#requires
|
||||
* @module ng
|
||||
* @returns {Array.<string>} List of module names which must be loaded before this module.
|
||||
*
|
||||
* @description
|
||||
* Holds the list of modules which the injector will load before the current module is
|
||||
* loaded.
|
||||
@@ -125,8 +125,9 @@ function setupModuleLoader(window) {
|
||||
* @ngdoc property
|
||||
* @name angular.Module#name
|
||||
* @module ng
|
||||
* @returns {string} Name of the module.
|
||||
*
|
||||
* @description
|
||||
* Name of the module.
|
||||
*/
|
||||
name: name,
|
||||
|
||||
|
||||
@@ -234,6 +234,13 @@ function Browser(window, document, $log, $sniffer) {
|
||||
return callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether the url has changed outside of Angular.
|
||||
* Needs to be exported to be able to check for changes that have been done in sync,
|
||||
* as hashchange/popstate events fire in async.
|
||||
*/
|
||||
self.$$checkUrlChange = fireUrlChange;
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// Misc API
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -67,8 +67,10 @@
|
||||
$scope.keys = [];
|
||||
$scope.cache = $cacheFactory('cacheId');
|
||||
$scope.put = function(key, value) {
|
||||
$scope.cache.put(key, value);
|
||||
$scope.keys.push(key);
|
||||
if ($scope.cache.get(key) === undefined) {
|
||||
$scope.keys.push(key);
|
||||
}
|
||||
$scope.cache.put(key, value === undefined ? null : value);
|
||||
};
|
||||
}]);
|
||||
</file>
|
||||
|
||||
+9
-7
@@ -191,9 +191,9 @@
|
||||
*
|
||||
* * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
|
||||
* * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
|
||||
* * `^` - Locate the required controller by searching the element's parents. Throw an error if not found.
|
||||
* * `?^` - Attempt to locate the required controller by searching the element's parents or pass `null` to the
|
||||
* `link` fn if not found.
|
||||
* * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
|
||||
* * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
|
||||
* `null` to the `link` fn if not found.
|
||||
*
|
||||
*
|
||||
* #### `controllerAs`
|
||||
@@ -214,7 +214,7 @@
|
||||
*
|
||||
* #### `template`
|
||||
* HTML markup that may:
|
||||
* * Replace the contents of the directive's element (defualt).
|
||||
* * Replace the contents of the directive's element (default).
|
||||
* * Replace the directive's element itself (if `replace` is true - DEPRECATED).
|
||||
* * Wrap the contents of the directive's element (if `transclude` is true).
|
||||
*
|
||||
@@ -1474,7 +1474,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
if (parentGet.literal) {
|
||||
compare = equals;
|
||||
} else {
|
||||
compare = function(a,b) { return a === b; };
|
||||
compare = function(a,b) { return a === b || (a !== a && b !== b); };
|
||||
}
|
||||
parentSet = parentGet.assign || function() {
|
||||
// reset the change, or we will throw this exception on every $digest
|
||||
@@ -2011,8 +2011,10 @@ function directiveNormalize(name) {
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $compile.directive.Attributes#$attr
|
||||
* @returns {object} A map of DOM element attribute names to the normalized name. This is
|
||||
* needed to do reverse lookup from normalized name back to actual name.
|
||||
*
|
||||
* @description
|
||||
* A map of DOM element attribute names to the normalized name. This is
|
||||
* needed to do reverse lookup from normalized name back to actual name.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
*
|
||||
* @description
|
||||
*
|
||||
* The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
|
||||
* We shouldn't do this, because it will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
|
||||
* ```html
|
||||
* <div ng-init="scope = { isDisabled: false }">
|
||||
* <button disabled="{{scope.isDisabled}}">Disabled</button>
|
||||
@@ -380,8 +380,12 @@ forEach(['src', 'srcset', 'href'], function(attrName) {
|
||||
}
|
||||
|
||||
attr.$observe(normalized, function(value) {
|
||||
if (!value)
|
||||
return;
|
||||
if (!value) {
|
||||
if (attrName === 'href') {
|
||||
attr.$set(name, null);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
attr.$set(name, value);
|
||||
|
||||
|
||||
@@ -70,8 +70,9 @@ function FormController(element, attrs, $scope, $animate) {
|
||||
// convenience method for easy toggling of classes
|
||||
function toggleValidCss(isValid, validationErrorKey) {
|
||||
validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
|
||||
$animate.removeClass(element, (isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey);
|
||||
$animate.addClass(element, (isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey);
|
||||
$animate.setClass(element,
|
||||
(isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey,
|
||||
(isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,8 +287,6 @@ function FormController(element, attrs, $scope, $animate) {
|
||||
* hitting enter in any of the input fields will trigger the click handler on the *first* button or
|
||||
* input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
|
||||
*
|
||||
* @param {string=} name Name of the form. If specified, the form controller will be published into
|
||||
* related scope, under this name.
|
||||
*
|
||||
* ## Animation Hooks
|
||||
*
|
||||
@@ -365,6 +364,8 @@ function FormController(element, attrs, $scope, $animate) {
|
||||
</file>
|
||||
</example>
|
||||
*
|
||||
* @param {string=} name Name of the form. If specified, the form controller will be published into
|
||||
* related scope, under this name.
|
||||
*/
|
||||
var formDirectiveFactory = function(isNgForm) {
|
||||
return ['$timeout', function($timeout) {
|
||||
|
||||
@@ -17,7 +17,9 @@ var inputType = {
|
||||
* @name input[text]
|
||||
*
|
||||
* @description
|
||||
* Standard HTML text input with angular data binding.
|
||||
* Standard HTML text input with angular data binding, inherited by most of the `input` elements.
|
||||
*
|
||||
* *NOTE* Not every feature offered is available for all input types.
|
||||
*
|
||||
* @param {string} ngModel Assignable angular expression to data-bind to.
|
||||
* @param {string=} name Property name of the form under which the control is published.
|
||||
@@ -35,6 +37,8 @@ var inputType = {
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
* @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
|
||||
* This parameter is ignored for input[type=password] controls, which will never trim the
|
||||
* input.
|
||||
*
|
||||
* @example
|
||||
<example name="text-input-directive" module="textInputExample">
|
||||
@@ -474,6 +478,7 @@ function addNativeHtml5Validators(ctrl, validatorName, badFlags, ignoreFlags, va
|
||||
function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
var validity = element.prop(VALIDITY_STATE_PROPERTY);
|
||||
var placeholder = element[0].placeholder, noevent = {};
|
||||
var type = lowercase(element[0].type);
|
||||
ctrl.$$validityState = validity;
|
||||
|
||||
// In composition mode, users are still inputing intermediate text buffer,
|
||||
@@ -507,8 +512,8 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
|
||||
// By default we will trim the value
|
||||
// If the attribute ng-trim exists we will avoid trimming
|
||||
// e.g. <input ng-model="foo" ng-trim="false">
|
||||
if (toBoolean(attr.ngTrim || 'T')) {
|
||||
// If input type is 'password', the value is never trimmed
|
||||
if (type !== 'password' && (toBoolean(attr.ngTrim || 'T'))) {
|
||||
value = trim(value);
|
||||
}
|
||||
|
||||
@@ -517,7 +522,7 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
// a row.
|
||||
var revalidate = validity && ctrl.$$hasNativeValidators;
|
||||
if (ctrl.$viewValue !== value || (value === '' && revalidate)) {
|
||||
if (scope.$$phase) {
|
||||
if (scope.$root.$$phase) {
|
||||
ctrl.$setViewValue(value);
|
||||
} else {
|
||||
scope.$apply(function() {
|
||||
@@ -783,6 +788,8 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
||||
* HTML input element control with angular data-binding. Input control follows HTML5 input types
|
||||
* and polyfills the HTML5 validation behavior for older browsers.
|
||||
*
|
||||
* *NOTE* Not every feature offered is available for all input types.
|
||||
*
|
||||
* @param {string} ngModel Assignable angular expression to data-bind to.
|
||||
* @param {string=} name Property name of the form under which the control is published.
|
||||
* @param {string=} required Sets `required` validation error key if the value is not entered.
|
||||
@@ -796,6 +803,9 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
||||
* patterns defined as scope expressions.
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
* @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
|
||||
* This parameter is ignored for input[type=password] controls, which will never trim the
|
||||
* input.
|
||||
*
|
||||
* @example
|
||||
<example name="input-directive" module="inputExample">
|
||||
|
||||
@@ -150,7 +150,6 @@ var ngBindTemplateDirective = ['$interpolate', function($interpolate) {
|
||||
* @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
|
||||
*
|
||||
* @example
|
||||
Try it here: enter text in text box and watch the greeting change.
|
||||
|
||||
<example module="bindHtmlExample" deps="angular-sanitize.js">
|
||||
<file name="index.html">
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
<button ng-click="count = count + 1" ng-init="count=0">
|
||||
Increment
|
||||
</button>
|
||||
count: {{count}}
|
||||
<span>
|
||||
count: {{count}}
|
||||
<span>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should check ng-click', function() {
|
||||
@@ -37,19 +39,33 @@
|
||||
* Events that are handled via these handler are always configured not to propagate further.
|
||||
*/
|
||||
var ngEventDirectives = {};
|
||||
|
||||
// For events that might fire synchronously during DOM manipulation
|
||||
// we need to execute their event handlers asynchronously using $evalAsync,
|
||||
// so that they are not executed in an inconsistent state.
|
||||
var forceAsyncEvents = {
|
||||
'blur': true,
|
||||
'focus': true
|
||||
};
|
||||
forEach(
|
||||
'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
|
||||
function(name) {
|
||||
var directiveName = directiveNormalize('ng-' + name);
|
||||
ngEventDirectives[directiveName] = ['$parse', function($parse) {
|
||||
ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
|
||||
return {
|
||||
compile: function($element, attr) {
|
||||
var fn = $parse(attr[directiveName]);
|
||||
return function ngEventHandler(scope, element) {
|
||||
element.on(lowercase(name), function(event) {
|
||||
scope.$apply(function() {
|
||||
var eventName = lowercase(name);
|
||||
element.on(eventName, function(event) {
|
||||
var callback = function() {
|
||||
fn(scope, {$event:event});
|
||||
});
|
||||
};
|
||||
if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
|
||||
scope.$evalAsync(callback);
|
||||
} else {
|
||||
scope.$apply(callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -307,6 +323,13 @@ forEach(
|
||||
* server and reloading the current page), but only if the form does not contain `action`,
|
||||
* `data-action`, or `x-action` attributes.
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
|
||||
* `ngSubmit` handlers together. See the
|
||||
* {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
|
||||
* for a detailed discussion of when `ngSubmit` may be triggered.
|
||||
* </div>
|
||||
*
|
||||
* @element form
|
||||
* @priority 0
|
||||
* @param {expression} ngSubmit {@link guide/expression Expression} to eval.
|
||||
@@ -359,6 +382,10 @@ forEach(
|
||||
* @description
|
||||
* Specify custom behavior on focus event.
|
||||
*
|
||||
* Note: As the `focus` event is executed synchronously when calling `input.focus()`
|
||||
* AngularJS executes the expression using `scope.$evalAsync` if the event is fired
|
||||
* during an `$apply` to ensure a consistent state.
|
||||
*
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
|
||||
@@ -375,6 +402,14 @@ forEach(
|
||||
* @description
|
||||
* Specify custom behavior on blur event.
|
||||
*
|
||||
* A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
|
||||
* an element has lost focus.
|
||||
*
|
||||
* Note: As the `blur` event is executed synchronously also during DOM manipulations
|
||||
* (e.g. removing a focussed input),
|
||||
* AngularJS executes the expression using `scope.$evalAsync` if the event is fired
|
||||
* during an `$apply` to ensure a consistent state.
|
||||
*
|
||||
* @element window, input, select, textarea, a
|
||||
* @priority 0
|
||||
* @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
|
||||
|
||||
@@ -309,8 +309,9 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
|
||||
if (block && block.scope) lastBlockMap[block.id] = block;
|
||||
});
|
||||
// This is a duplicate and we need to throw an error
|
||||
throw ngRepeatMinErr('dupes', "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}",
|
||||
expression, trackById);
|
||||
throw ngRepeatMinErr('dupes',
|
||||
"Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
|
||||
expression, trackById, toJson(value));
|
||||
} else {
|
||||
// new never before seen block
|
||||
nextBlockOrder[index] = { id: trackById };
|
||||
|
||||
+27
-21
@@ -395,21 +395,37 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
value = valueFn(scope, locals);
|
||||
}
|
||||
}
|
||||
// Update the null option's selected property here so $render cleans it up correctly
|
||||
if (optionGroupsCache[0].length > 1) {
|
||||
if (optionGroupsCache[0][1].id !== key) {
|
||||
optionGroupsCache[0][1].selected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctrl.$setViewValue(value);
|
||||
render();
|
||||
});
|
||||
});
|
||||
|
||||
ctrl.$render = render;
|
||||
|
||||
// TODO(vojta): can't we optimize this ?
|
||||
scope.$watch(render);
|
||||
scope.$watchCollection(valuesFn, render);
|
||||
if ( multiple ) {
|
||||
scope.$watchCollection(function() { return ctrl.$modelValue; }, render);
|
||||
}
|
||||
|
||||
function getSelectedSet() {
|
||||
var selectedSet = false;
|
||||
if (multiple) {
|
||||
var modelValue = ctrl.$modelValue;
|
||||
if (trackFn && isArray(modelValue)) {
|
||||
selectedSet = new HashMap([]);
|
||||
var locals = {};
|
||||
for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
|
||||
locals[valueName] = modelValue[trackIndex];
|
||||
selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
|
||||
}
|
||||
} else {
|
||||
selectedSet = new HashMap(modelValue);
|
||||
}
|
||||
}
|
||||
return selectedSet;
|
||||
}
|
||||
|
||||
|
||||
function render() {
|
||||
// Temporary location for the option groups before we render them
|
||||
@@ -427,22 +443,11 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
groupIndex, index,
|
||||
locals = {},
|
||||
selected,
|
||||
selectedSet = false, // nothing is selected yet
|
||||
selectedSet = getSelectedSet(),
|
||||
lastElement,
|
||||
element,
|
||||
label;
|
||||
|
||||
if (multiple) {
|
||||
if (trackFn && isArray(modelValue)) {
|
||||
selectedSet = new HashMap([]);
|
||||
for (var trackIndex = 0; trackIndex < modelValue.length; trackIndex++) {
|
||||
locals[valueName] = modelValue[trackIndex];
|
||||
selectedSet.put(trackFn(scope, locals), modelValue[trackIndex]);
|
||||
}
|
||||
} else {
|
||||
selectedSet = new HashMap(modelValue);
|
||||
}
|
||||
}
|
||||
|
||||
// We now build up the list of options we need (we merge later)
|
||||
for (index = 0; length = keys.length, index < length; index++) {
|
||||
@@ -538,7 +543,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
lastElement.val(existingOption.id = option.id);
|
||||
}
|
||||
// lastElement.prop('selected') provided by jQuery has side-effects
|
||||
if (existingOption.selected !== option.selected) {
|
||||
if (lastElement[0].selected !== option.selected) {
|
||||
lastElement.prop('selected', (existingOption.selected = option.selected));
|
||||
if (msie) {
|
||||
// See #7692
|
||||
@@ -561,6 +566,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
(element = optionTemplate.clone())
|
||||
.val(option.id)
|
||||
.prop('selected', option.selected)
|
||||
.attr('selected', option.selected)
|
||||
.text(option.label);
|
||||
}
|
||||
|
||||
|
||||
+1
-11
@@ -60,16 +60,6 @@
|
||||
* For more information about how angular filters work, and how to create your own filters, see
|
||||
* {@link guide/filter Filters} in the Angular Developer Guide.
|
||||
*/
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $filterProvider#register
|
||||
* @description
|
||||
* Register filter factory function.
|
||||
*
|
||||
* @param {String} name Name of the filter.
|
||||
* @param {Function} fn The filter factory function which is injectable.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* @ngdoc service
|
||||
@@ -108,7 +98,7 @@ function $FilterProvider($provide) {
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name $controllerProvider#register
|
||||
* @name $filterProvider#register
|
||||
* @param {string|Object} name Name of the filter function, or an object map of filters where
|
||||
* the keys are the filter names and the values are the filter factories.
|
||||
* @returns {Object} Registered filter instance, or if a map of filters was provided then a map
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
* which have property `name` containing "M" and property `phone` containing "1". A special
|
||||
* property name `$` can be used (as in `{$:"text"}`) to accept a match against any
|
||||
* property of the object. That's equivalent to the simple substring match with a `string`
|
||||
* as described above.
|
||||
* as described above. The predicate can be negated by prefixing the string with `!`.
|
||||
* For Example `{name: "!M"}` predicate will return an array of items which have property `name`
|
||||
* not containing "M".
|
||||
*
|
||||
* - `function(value)`: A predicate function can be used to write arbitrary filters. The function is
|
||||
* called for each element of `array`. The final result is an array of those elements that
|
||||
|
||||
@@ -153,6 +153,10 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
|
||||
number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
|
||||
|
||||
if (number === 0) {
|
||||
isNegative = false;
|
||||
}
|
||||
|
||||
var fraction = ('' + number).split(DECIMAL_SEP);
|
||||
var whole = fraction[0];
|
||||
fraction = fraction[1] || '';
|
||||
@@ -322,12 +326,12 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
* * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm)
|
||||
* * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm)
|
||||
*
|
||||
* `format` string can contain literal values. These need to be quoted with single quotes (e.g.
|
||||
* `"h 'in the morning'"`). In order to output single quote, use two single quotes in a sequence
|
||||
* `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
|
||||
* `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
|
||||
* (e.g. `"h 'o''clock'"`).
|
||||
*
|
||||
* @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
|
||||
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and its
|
||||
* number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
|
||||
* shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
|
||||
* specified in the string input, the time is considered to be in the local timezone.
|
||||
* @param {string=} format Formatting rules (see Description). If not specified,
|
||||
@@ -343,6 +347,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
<span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
|
||||
<span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
|
||||
<span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
|
||||
<span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
|
||||
<span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should format date', function() {
|
||||
@@ -352,6 +358,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
|
||||
expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
|
||||
toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
|
||||
expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
|
||||
toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
|
||||
@@ -18,9 +18,13 @@
|
||||
*
|
||||
* - `function`: Getter function. The result of this function will be sorted using the
|
||||
* `<`, `=`, `>` operator.
|
||||
* - `string`: An Angular expression which evaluates to an object to order by, such as 'name'
|
||||
* to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control
|
||||
* ascending or descending sort order (for example, +name or -name).
|
||||
* - `string`: An Angular expression. The result of this expression is used to compare elements
|
||||
* (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
|
||||
* 3 first characters of a property called `name`). The result of a constant expression
|
||||
* is interpreted as a property name to be used in comparisons (for example `"special name"`
|
||||
* to sort object by the value of their `special name` property). An expression can be
|
||||
* optionally prefixed with `+` or `-` to control ascending or descending sort order
|
||||
* (for example, `+name` or `-name`).
|
||||
* - `Array`: An array of function or string predicates. The first predicate in the array
|
||||
* is used for sorting, but when two items are equivalent, the next predicate is used.
|
||||
*
|
||||
@@ -111,7 +115,7 @@
|
||||
orderByFilter.$inject = ['$parse'];
|
||||
function orderByFilter($parse){
|
||||
return function(array, sortPredicate, reverseOrder) {
|
||||
if (!isArray(array)) return array;
|
||||
if (!(isArrayLike(array))) return array;
|
||||
if (!sortPredicate) return array;
|
||||
sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate];
|
||||
sortPredicate = map(sortPredicate, function(predicate){
|
||||
|
||||
+6
-4
@@ -275,6 +275,7 @@ function $HttpProvider() {
|
||||
* - {@link ng.$http#put $http.put}
|
||||
* - {@link ng.$http#delete $http.delete}
|
||||
* - {@link ng.$http#jsonp $http.jsonp}
|
||||
* - {@link ng.$http#patch $http.patch}
|
||||
*
|
||||
*
|
||||
* # Setting HTTP Headers
|
||||
@@ -539,7 +540,7 @@ function $HttpProvider() {
|
||||
* that only JavaScript running on your domain could have sent the request. The token must be
|
||||
* unique for each user and must be verifiable by the server (to prevent the JavaScript from
|
||||
* making up its own tokens). We recommend that the token is a digest of your site's
|
||||
* authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
|
||||
* authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
|
||||
* for added security.
|
||||
*
|
||||
* The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
|
||||
@@ -576,7 +577,7 @@ function $HttpProvider() {
|
||||
* - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
|
||||
* that should abort the request when resolved.
|
||||
* - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
|
||||
* XHR object. See [requests with credentials]https://developer.mozilla.org/en/http_access_control#section_5
|
||||
* XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
|
||||
* for more information.
|
||||
* - **responseType** - `{string}` - see
|
||||
* [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
|
||||
@@ -945,7 +946,8 @@ function $HttpProvider() {
|
||||
promise.then(removePendingReq, removePendingReq);
|
||||
|
||||
|
||||
if ((config.cache || defaults.cache) && config.cache !== false && config.method == 'GET') {
|
||||
if ((config.cache || defaults.cache) && config.cache !== false &&
|
||||
(config.method === 'GET' || config.method === 'JSONP')) {
|
||||
cache = isObject(config.cache) ? config.cache
|
||||
: isObject(defaults.cache) ? defaults.cache
|
||||
: defaultCache;
|
||||
@@ -1046,7 +1048,7 @@ function $HttpProvider() {
|
||||
if (isObject(v)) {
|
||||
if (isDate(v)){
|
||||
v = v.toISOString();
|
||||
} else if (isObject(v)) {
|
||||
} else {
|
||||
v = toJson(v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ function $InterpolateProvider() {
|
||||
* @description
|
||||
* Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
|
||||
*
|
||||
* Use {@link ng.$interpolateProvider#startSymbol $interpolateProvider#startSymbol} to change
|
||||
* Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
|
||||
* the symbol.
|
||||
*
|
||||
* @returns {string} start symbol.
|
||||
@@ -237,7 +237,7 @@ function $InterpolateProvider() {
|
||||
* @description
|
||||
* Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
|
||||
*
|
||||
* Use {@link ng.$interpolateProvider#endSymbol $interpolateProvider#endSymbol} to change
|
||||
* Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
|
||||
* the symbol.
|
||||
*
|
||||
* @returns {string} end symbol.
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ function $IntervalProvider() {
|
||||
* };
|
||||
*
|
||||
* $scope.$on('$destroy', function() {
|
||||
* // Make sure that the interval nis destroyed too
|
||||
* // Make sure that the interval is destroyed too
|
||||
* $scope.stopFight();
|
||||
* });
|
||||
* }])
|
||||
|
||||
+18
-9
@@ -320,17 +320,16 @@ LocationHashbangInHtml5Url.prototype =
|
||||
* Change path, search and hash, when called with parameter and return `$location`.
|
||||
*
|
||||
* @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
|
||||
* @param {string=} replace The path that will be changed
|
||||
* @return {string} url
|
||||
*/
|
||||
url: function(url, replace) {
|
||||
url: function(url) {
|
||||
if (isUndefined(url))
|
||||
return this.$$url;
|
||||
|
||||
var match = PATH_MATCH.exec(url);
|
||||
if (match[1]) this.path(decodeURIComponent(match[1]));
|
||||
if (match[2] || match[1]) this.search(match[3] || '');
|
||||
this.hash(match[5] || '', replace);
|
||||
this.hash(match[5] || '');
|
||||
|
||||
return this;
|
||||
},
|
||||
@@ -388,10 +387,11 @@ LocationHashbangInHtml5Url.prototype =
|
||||
* Note: Path should always begin with forward slash (/), this method will add the forward slash
|
||||
* if it is missing.
|
||||
*
|
||||
* @param {string=} path New path
|
||||
* @param {(string|number)=} path New path
|
||||
* @return {string} path
|
||||
*/
|
||||
path: locationGetterSetter('$$path', function(path) {
|
||||
path = path ? path.toString() : '';
|
||||
return path.charAt(0) == '/' ? path : '/' + path;
|
||||
}),
|
||||
|
||||
@@ -427,7 +427,7 @@ LocationHashbangInHtml5Url.prototype =
|
||||
* If the argument is a hash object containing an array of values, these values will be encoded
|
||||
* as duplicate search parameters in the url.
|
||||
*
|
||||
* @param {(string|Array<string>|boolean)=} paramValue If `search` is a string, then `paramValue`
|
||||
* @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
|
||||
* will override only a single search property.
|
||||
*
|
||||
* If `paramValue` is an array, it will override the property of the `search` component of
|
||||
@@ -446,7 +446,8 @@ LocationHashbangInHtml5Url.prototype =
|
||||
case 0:
|
||||
return this.$$search;
|
||||
case 1:
|
||||
if (isString(search)) {
|
||||
if (isString(search) || isNumber(search)) {
|
||||
search = search.toString();
|
||||
this.$$search = parseKeyValue(search);
|
||||
} else if (isObject(search)) {
|
||||
// remove object undefined or null properties
|
||||
@@ -483,10 +484,12 @@ LocationHashbangInHtml5Url.prototype =
|
||||
*
|
||||
* Change hash fragment when called with parameter and return `$location`.
|
||||
*
|
||||
* @param {string=} hash New hash fragment
|
||||
* @param {(string|number)=} hash New hash fragment
|
||||
* @return {string} hash
|
||||
*/
|
||||
hash: locationGetterSetter('$$hash', identity),
|
||||
hash: locationGetterSetter('$$hash', function(hash) {
|
||||
return hash ? hash.toString() : '';
|
||||
}),
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
@@ -635,6 +638,8 @@ function $LocationProvider(){
|
||||
$location = new LocationMode(appBase, '#' + hashPrefix);
|
||||
$location.$$parse($location.$$rewrite(initialUrl));
|
||||
|
||||
var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
|
||||
|
||||
$rootElement.on('click', function(event) {
|
||||
// TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
|
||||
// currently we open nice url link and redirect then
|
||||
@@ -657,6 +662,9 @@ function $LocationProvider(){
|
||||
absHref = urlResolve(absHref.animVal).href;
|
||||
}
|
||||
|
||||
// Ignore when url is started with javascript: or mailto:
|
||||
if (IGNORE_URI_REGEXP.test(absHref)) return;
|
||||
|
||||
// Make relative links work in HTML5 mode for legacy browsers (or at least IE8 & 9)
|
||||
// The href should be a regular url e.g. /link/somewhere or link/somewhere or ../somewhere or
|
||||
// somewhere#anchor or http://example.com/somewhere
|
||||
@@ -665,7 +673,7 @@ function $LocationProvider(){
|
||||
// http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
|
||||
var href = elm.attr('href') || elm.attr('xlink:href');
|
||||
|
||||
if (href.indexOf('://') < 0) { // Ignore absolute URLs
|
||||
if (href && href.indexOf('://') < 0) { // Ignore absolute URLs
|
||||
var prefix = '#' + hashPrefix;
|
||||
if (href[0] == '/') {
|
||||
// absolute path - replace old path
|
||||
@@ -677,6 +685,7 @@ function $LocationProvider(){
|
||||
// relative path - join with current path
|
||||
var stack = $location.path().split("/"),
|
||||
parts = href.split("/");
|
||||
if (stack.length === 2 && !stack[1]) stack.length = 1;
|
||||
for (var i=0; i<parts.length; i++) {
|
||||
if (parts[i] == ".")
|
||||
continue;
|
||||
|
||||
+13
-9
@@ -606,9 +606,9 @@ Parser.prototype = {
|
||||
var middle;
|
||||
var token;
|
||||
if ((token = this.expect('?'))) {
|
||||
middle = this.ternary();
|
||||
middle = this.assignment();
|
||||
if ((token = this.expect(':'))) {
|
||||
return this.ternaryFn(left, middle, this.ternary());
|
||||
return this.ternaryFn(left, middle, this.assignment());
|
||||
} else {
|
||||
this.throwError('expected :', token);
|
||||
}
|
||||
@@ -696,7 +696,9 @@ Parser.prototype = {
|
||||
return getter(self || object(scope, locals));
|
||||
}, {
|
||||
assign: function(scope, value, locals) {
|
||||
return setter(object(scope, locals), field, value, parser.text, parser.options);
|
||||
var o = object(scope, locals);
|
||||
if (!o) object.assign(scope, o = {});
|
||||
return setter(o, field, value, parser.text, parser.options);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -726,10 +728,11 @@ Parser.prototype = {
|
||||
return v;
|
||||
}, {
|
||||
assign: function(self, value, locals) {
|
||||
var key = indexFn(self, locals);
|
||||
var key = ensureSafeMemberName(indexFn(self, locals), parser.text);
|
||||
// prevent overwriting of Function.constructor which would break ensureSafeObject check
|
||||
var safe = ensureSafeObject(obj(self, locals), parser.text);
|
||||
return safe[key] = value;
|
||||
var o = ensureSafeObject(obj(self, locals), parser.text);
|
||||
if (!o) obj.assign(self, o = {});
|
||||
return o[key] = value;
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -750,7 +753,7 @@ Parser.prototype = {
|
||||
var context = contextGetter ? contextGetter(scope, locals) : scope;
|
||||
|
||||
for (var i = 0; i < argsFn.length; i++) {
|
||||
args.push(argsFn[i](scope, locals));
|
||||
args.push(ensureSafeObject(argsFn[i](scope, locals), parser.text));
|
||||
}
|
||||
var fnPtr = fn(scope, locals, context) || noop;
|
||||
|
||||
@@ -838,13 +841,15 @@ Parser.prototype = {
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
function setter(obj, path, setValue, fullExp, options) {
|
||||
ensureSafeObject(obj, fullExp);
|
||||
|
||||
//needed?
|
||||
options = options || {};
|
||||
|
||||
var element = path.split('.'), key;
|
||||
for (var i = 0; element.length > 1; i++) {
|
||||
key = ensureSafeMemberName(element.shift(), fullExp);
|
||||
var propertyObj = obj[key];
|
||||
var propertyObj = ensureSafeObject(obj[key], fullExp);
|
||||
if (!propertyObj) {
|
||||
propertyObj = {};
|
||||
obj[key] = propertyObj;
|
||||
@@ -864,7 +869,6 @@ function setter(obj, path, setValue, fullExp, options) {
|
||||
}
|
||||
}
|
||||
key = ensureSafeMemberName(element.shift(), fullExp);
|
||||
ensureSafeObject(obj, fullExp);
|
||||
ensureSafeObject(obj[key], fullExp);
|
||||
obj[key] = setValue;
|
||||
return setValue;
|
||||
|
||||
+22
-5
@@ -139,10 +139,26 @@ function $RootScopeProvider(){
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $rootScope.Scope#$id
|
||||
* @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for
|
||||
* debugging.
|
||||
*
|
||||
* @description
|
||||
* Unique scope ID (monotonically increasing) useful for debugging.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $rootScope.Scope#$parent
|
||||
*
|
||||
* @description
|
||||
* Reference to the parent scope.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $rootScope.Scope#$root
|
||||
*
|
||||
* @description
|
||||
* Reference to the root scope.
|
||||
*/
|
||||
|
||||
Scope.prototype = {
|
||||
constructor: Scope,
|
||||
@@ -154,9 +170,8 @@ function $RootScopeProvider(){
|
||||
* @description
|
||||
* Creates a new child {@link ng.$rootScope.Scope scope}.
|
||||
*
|
||||
* The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and
|
||||
* {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the
|
||||
* scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
|
||||
* The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
|
||||
* The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
|
||||
*
|
||||
* {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
|
||||
* desired for the scope and its child scopes to be permanently detached from the parent and
|
||||
@@ -609,6 +624,8 @@ function $RootScopeProvider(){
|
||||
logIdx, logMsg, asyncTask;
|
||||
|
||||
beginPhase('$digest');
|
||||
// Check for changes to browser url that happened in sync before the call to $digest
|
||||
$browser.$$checkUrlChange();
|
||||
|
||||
lastDirtyWatch = null;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*/
|
||||
function $$SanitizeUriProvider() {
|
||||
var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
|
||||
imgSrcSanitizationWhitelist = /^\s*(https?|ftp|file):|data:image\//;
|
||||
imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file):|data:image\/)/;
|
||||
|
||||
/**
|
||||
* @description
|
||||
|
||||
Vendored
+5
-1
@@ -56,6 +56,8 @@ angular.mock.$Browser = function() {
|
||||
return listener;
|
||||
};
|
||||
|
||||
self.$$checkUrlChange = angular.noop;
|
||||
|
||||
self.cookieHash = {};
|
||||
self.lastCookieHash = {};
|
||||
self.deferredFns = [];
|
||||
@@ -1779,7 +1781,7 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
||||
* use the `passThrough` request handler of `when` instead of `respond`.
|
||||
*
|
||||
* Additionally, we don't want to manually have to flush mocked out requests like we do during unit
|
||||
* testing. For this reason the e2e $httpBackend automatically flushes mocked out requests
|
||||
* testing. For this reason the e2e $httpBackend flushes mocked out requests
|
||||
* automatically, closely simulating the behavior of the XMLHttpRequest object.
|
||||
*
|
||||
* To setup the application to run with this http backend, you have to create a module that depends
|
||||
@@ -1996,6 +1998,7 @@ if(window.jasmine || window.mocha) {
|
||||
* @description
|
||||
*
|
||||
* *NOTE*: This function is also published on window for easy access.<br>
|
||||
* *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
|
||||
*
|
||||
* This function registers a module configuration code. It collects the configuration information
|
||||
* which will be used when the injector is created by {@link angular.mock.inject inject}.
|
||||
@@ -2038,6 +2041,7 @@ if(window.jasmine || window.mocha) {
|
||||
* @description
|
||||
*
|
||||
* *NOTE*: This function is also published on window for easy access.<br>
|
||||
* *NOTE*: This function is declared ONLY WHEN running tests with jasmine or mocha
|
||||
*
|
||||
* The inject function wraps a function into an injectable function. The inject() creates new
|
||||
* instance of {@link auto.$injector $injector} per test, which is then used for
|
||||
|
||||
@@ -94,8 +94,10 @@ function shallowClearAndCopy(src, dst) {
|
||||
* Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
|
||||
* URL `/path/greet?salutation=Hello`.
|
||||
*
|
||||
* If the parameter value is prefixed with `@` then the value of that parameter will be taken
|
||||
* from the corresponding key on the data object (useful for non-GET operations).
|
||||
* If the parameter value is prefixed with `@` then the value for that parameter will be extracted
|
||||
* from the corresponding property on the `data` object (provided when calling an action method). For
|
||||
* example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
|
||||
* will be `data.someProp`.
|
||||
*
|
||||
* @param {Object.<Object>=} actions Hash with declaration of custom action that should extend
|
||||
* the default set of resource actions. The declaration should be created in the format of {@link
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
*/
|
||||
angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
|
||||
var LINKY_URL_REGEXP =
|
||||
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>]/,
|
||||
/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"]/,
|
||||
MAILTO_REGEXP = /^mailto:/;
|
||||
|
||||
return function(text, target) {
|
||||
|
||||
@@ -232,6 +232,13 @@ function makeMap(str) {
|
||||
* @param {object} handler
|
||||
*/
|
||||
function htmlParser( html, handler ) {
|
||||
if (typeof html !== 'string') {
|
||||
if (html === null || typeof html === 'undefined') {
|
||||
html = '';
|
||||
} else {
|
||||
html = '' + html;
|
||||
}
|
||||
}
|
||||
var index, chars, match, stack = [], last = html, text;
|
||||
stack.last = function() { return stack[ stack.length - 1 ]; };
|
||||
|
||||
|
||||
+12
-1
@@ -174,6 +174,12 @@ describe('angular', function() {
|
||||
expect(aCopy).toBe(aCopy.self);
|
||||
expect(aCopy.selfs[2]).not.toBe(a.selfs[2]);
|
||||
});
|
||||
|
||||
it('should clear destination arrays correctly when source is non-array', function() {
|
||||
expect(copy(null, [1,2,3])).toEqual([]);
|
||||
expect(copy(undefined, [1,2,3])).toEqual([]);
|
||||
expect(copy({0: 1, 1: 2}, [1,2,3])).toEqual([1,2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("extend", function() {
|
||||
@@ -360,6 +366,11 @@ describe('angular', function() {
|
||||
expect(equals(new Date(0), new Date(1))).toBe(false);
|
||||
expect(equals(new Date(0), 0)).toBe(false);
|
||||
expect(equals(0, new Date(0))).toBe(false);
|
||||
|
||||
expect(equals(new Date(undefined), new Date(undefined))).toBe(true);
|
||||
expect(equals(new Date(undefined), new Date(0))).toBe(false);
|
||||
expect(equals(new Date(undefined), new Date(null))).toBe(false);
|
||||
expect(equals(new Date(undefined), new Date('wrong'))).toBe(true);
|
||||
});
|
||||
|
||||
it('should correctly test for keys that are present on Object.prototype', function() {
|
||||
@@ -822,7 +833,7 @@ describe('angular', function() {
|
||||
expect(function () {
|
||||
angular.bootstrap(element);
|
||||
}).toThrowMatching(
|
||||
/\[ng:btstrpd\] App Already Bootstrapped with this Element '<div class="?ng\-scope"?( ng[0-9]+="?[0-9]+"?)?>'/i
|
||||
/\[ng:btstrpd\] App Already Bootstrapped with this Element '<div class="?ng\-scope"?( ng[0-9]+="?[0-9]+"?)?>'/i
|
||||
);
|
||||
|
||||
dealoc(element);
|
||||
|
||||
+21
-1
@@ -1647,9 +1647,11 @@ describe('jqLite', function() {
|
||||
element.triggerHandler('click');
|
||||
event = pokeSpy.mostRecentCall.args[0];
|
||||
expect(event.preventDefault).toBeDefined();
|
||||
expect(event.target).toEqual(element[0]);
|
||||
expect(event.type).toEqual('click');
|
||||
});
|
||||
|
||||
it('should pass data as an additional argument', function() {
|
||||
it('should pass extra parameters as an additional argument', function() {
|
||||
var element = jqLite('<a>poke</a>'),
|
||||
pokeSpy = jasmine.createSpy('poke'),
|
||||
data;
|
||||
@@ -1680,6 +1682,24 @@ describe('jqLite', function() {
|
||||
expect(clickOnceSpy).toHaveBeenCalledOnce();
|
||||
expect(clickSpy.callCount).toBe(2);
|
||||
});
|
||||
|
||||
it("should accept a custom event instead of eventName", function() {
|
||||
var element = jqLite('<a>poke</a>'),
|
||||
pokeSpy = jasmine.createSpy('poke'),
|
||||
customEvent = {
|
||||
type: 'click',
|
||||
someProp: 'someValue'
|
||||
},
|
||||
actualEvent;
|
||||
|
||||
element.on('click', pokeSpy);
|
||||
element.triggerHandler(customEvent);
|
||||
actualEvent = pokeSpy.mostRecentCall.args[0];
|
||||
expect(actualEvent.preventDefault).toBeDefined();
|
||||
expect(actualEvent.someProp).toEqual('someValue');
|
||||
expect(actualEvent.target).toEqual(element[0]);
|
||||
expect(actualEvent.type).toEqual('click');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
+32
-4
@@ -36,7 +36,7 @@ function MockWindow() {
|
||||
};
|
||||
|
||||
this.location = {
|
||||
href: 'http://server',
|
||||
href: 'http://server/',
|
||||
replace: noop
|
||||
};
|
||||
|
||||
@@ -414,7 +414,7 @@ describe('browser', function() {
|
||||
|
||||
expect(replaceState).not.toHaveBeenCalled();
|
||||
expect(locationReplace).not.toHaveBeenCalled();
|
||||
expect(fakeWindow.location.href).toEqual('http://server');
|
||||
expect(fakeWindow.location.href).toEqual('http://server/');
|
||||
});
|
||||
|
||||
it('should use history.replaceState when available', function() {
|
||||
@@ -426,7 +426,7 @@ describe('browser', function() {
|
||||
|
||||
expect(pushState).not.toHaveBeenCalled();
|
||||
expect(locationReplace).not.toHaveBeenCalled();
|
||||
expect(fakeWindow.location.href).toEqual('http://server');
|
||||
expect(fakeWindow.location.href).toEqual('http://server/');
|
||||
});
|
||||
|
||||
it('should set location.href when pushState not available', function() {
|
||||
@@ -448,7 +448,7 @@ describe('browser', function() {
|
||||
|
||||
expect(pushState).not.toHaveBeenCalled();
|
||||
expect(replaceState).not.toHaveBeenCalled();
|
||||
expect(fakeWindow.location.href).toEqual('http://server');
|
||||
expect(fakeWindow.location.href).toEqual('http://server/');
|
||||
});
|
||||
|
||||
it('should return $browser to allow chaining', function() {
|
||||
@@ -615,4 +615,32 @@ describe('browser', function() {
|
||||
expect(browser.baseHref()).toEqual('/base/path/');
|
||||
});
|
||||
});
|
||||
|
||||
describe('integration tests with $location', function() {
|
||||
|
||||
beforeEach(module(function($provide, $locationProvider) {
|
||||
spyOn(fakeWindow.history, 'pushState').andCallFake(function(stateObj, title, newUrl) {
|
||||
fakeWindow.location.href = newUrl;
|
||||
});
|
||||
$provide.value('$browser', browser);
|
||||
browser.pollFns = [];
|
||||
|
||||
$locationProvider.html5Mode(true);
|
||||
}));
|
||||
|
||||
it('should update $location when it was changed outside of Angular in sync '+
|
||||
'before $digest was called', function() {
|
||||
inject(function($rootScope, $location) {
|
||||
fakeWindow.history.pushState(null, '', 'http://server/someTestHash');
|
||||
|
||||
// Verify that infinite digest reported in #6976 no longer occurs
|
||||
expect(function() {
|
||||
$rootScope.$digest();
|
||||
}).not.toThrow();
|
||||
|
||||
expect($location.path()).toBe('/someTestHash');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -2671,6 +2671,28 @@ describe('$compile', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should update parent scope when "="-bound NaN changes', inject(function($compile, $rootScope) {
|
||||
$rootScope.num = NaN;
|
||||
compile('<div my-component reference="num"></div>');
|
||||
var isolateScope = element.isolateScope();
|
||||
expect(isolateScope.reference).toBeNaN();
|
||||
|
||||
isolateScope.$apply(function(scope) { scope.reference = 64; });
|
||||
expect($rootScope.num).toBe(64);
|
||||
}));
|
||||
|
||||
|
||||
it('should update isolate scope when "="-bound NaN changes', inject(function($compile, $rootScope) {
|
||||
$rootScope.num = NaN;
|
||||
compile('<div my-component reference="num"></div>');
|
||||
var isolateScope = element.isolateScope();
|
||||
expect(isolateScope.reference).toBeNaN();
|
||||
|
||||
$rootScope.$apply(function(scope) { scope.num = 64; });
|
||||
expect(isolateScope.reference).toBe(64);
|
||||
}));
|
||||
|
||||
|
||||
describe('attribute', function() {
|
||||
it('should copy simple attribute', inject(function() {
|
||||
compile('<div><span my-component attr="some text">');
|
||||
|
||||
@@ -252,6 +252,23 @@ describe('ngHref', function() {
|
||||
expect(element.attr('href')).toEqual('http://server');
|
||||
}));
|
||||
|
||||
it('should not set the href if ng-href is empty', inject(function($rootScope, $compile) {
|
||||
$rootScope.url = null;
|
||||
element = $compile('<a ng-href="{{url}}"></a>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('href')).toEqual(undefined);
|
||||
}));
|
||||
|
||||
it('should remove the href if ng-href changes to empty', inject(function($rootScope, $compile) {
|
||||
$rootScope.url = 'http://www.google.com/';
|
||||
element = $compile('<a ng-href="{{url}}"></a>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
$rootScope.url = null;
|
||||
$rootScope.$digest();
|
||||
expect(element.attr('href')).toEqual(undefined);
|
||||
}));
|
||||
|
||||
if (isDefined(window.SVGElement)) {
|
||||
describe('SVGAElement', function() {
|
||||
it('should interpolate the expression and bind to xlink:href', inject(function($compile, $rootScope) {
|
||||
|
||||
@@ -621,10 +621,8 @@ describe('form animations', function() {
|
||||
it('should trigger an animation when invalid', inject(function($animate) {
|
||||
form.$setValidity('required', false);
|
||||
|
||||
assertValidAnimation($animate.queue[0], 'removeClass', 'ng-valid');
|
||||
assertValidAnimation($animate.queue[1], 'addClass', 'ng-invalid');
|
||||
assertValidAnimation($animate.queue[2], 'removeClass', 'ng-valid-required');
|
||||
assertValidAnimation($animate.queue[3], 'addClass', 'ng-invalid-required');
|
||||
assertValidAnimation($animate.queue[0], 'setClass', 'ng-invalid', 'ng-valid');
|
||||
assertValidAnimation($animate.queue[1], 'setClass', 'ng-invalid-required', 'ng-valid-required');
|
||||
}));
|
||||
|
||||
it('should trigger an animation when valid', inject(function($animate) {
|
||||
@@ -634,10 +632,8 @@ describe('form animations', function() {
|
||||
|
||||
form.$setValidity('required', true);
|
||||
|
||||
assertValidAnimation($animate.queue[0], 'removeClass', 'ng-invalid');
|
||||
assertValidAnimation($animate.queue[1], 'addClass', 'ng-valid');
|
||||
assertValidAnimation($animate.queue[2], 'removeClass', 'ng-invalid-required');
|
||||
assertValidAnimation($animate.queue[3], 'addClass', 'ng-valid-required');
|
||||
assertValidAnimation($animate.queue[0], 'setClass', 'ng-valid', 'ng-invalid');
|
||||
assertValidAnimation($animate.queue[1], 'setClass', 'ng-valid-required', 'ng-invalid-required');
|
||||
}));
|
||||
|
||||
it('should trigger an animation when dirty', inject(function($animate) {
|
||||
@@ -661,17 +657,13 @@ describe('form animations', function() {
|
||||
it('should trigger custom errors as addClass/removeClass when invalid/valid', inject(function($animate) {
|
||||
form.$setValidity('custom-error', false);
|
||||
|
||||
assertValidAnimation($animate.queue[0], 'removeClass', 'ng-valid');
|
||||
assertValidAnimation($animate.queue[1], 'addClass', 'ng-invalid');
|
||||
assertValidAnimation($animate.queue[2], 'removeClass', 'ng-valid-custom-error');
|
||||
assertValidAnimation($animate.queue[3], 'addClass', 'ng-invalid-custom-error');
|
||||
assertValidAnimation($animate.queue[0], 'setClass', 'ng-invalid', 'ng-valid');
|
||||
assertValidAnimation($animate.queue[1], 'setClass', 'ng-invalid-custom-error', 'ng-valid-custom-error');
|
||||
|
||||
$animate.queue = [];
|
||||
form.$setValidity('custom-error', true);
|
||||
|
||||
assertValidAnimation($animate.queue[0], 'removeClass', 'ng-invalid');
|
||||
assertValidAnimation($animate.queue[1], 'addClass', 'ng-valid');
|
||||
assertValidAnimation($animate.queue[2], 'removeClass', 'ng-invalid-custom-error');
|
||||
assertValidAnimation($animate.queue[3], 'addClass', 'ng-valid-custom-error');
|
||||
assertValidAnimation($animate.queue[0], 'setClass', 'ng-valid', 'ng-invalid');
|
||||
assertValidAnimation($animate.queue[1], 'setClass', 'ng-valid-custom-error', 'ng-invalid-custom-error');
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -580,19 +580,30 @@ describe('input', function() {
|
||||
});
|
||||
|
||||
if (!_jqLiteMode) {
|
||||
it('should not cause the double $digest when triggering an event using jQuery', function() {
|
||||
$sniffer.hasEvent = function(eventName) {
|
||||
return eventName !== 'input';
|
||||
};
|
||||
describe('double $digest when triggering an event using jQuery', function() {
|
||||
function run() {
|
||||
$sniffer.hasEvent = function(eventName) {
|
||||
return eventName !== 'input';
|
||||
};
|
||||
|
||||
compileInput('<input type="text" ng-model="name" name="alias" ng-change="change()" />');
|
||||
compileInput('<input type="text" ng-model="name" name="alias" ng-change="change()" />');
|
||||
|
||||
scope.field = 'fake field';
|
||||
scope.$watch('field', function() {
|
||||
// We need to use _originalTrigger since trigger is modified by Angular Scenario.
|
||||
inputElm._originalTrigger('change');
|
||||
scope.field = 'fake field';
|
||||
scope.$watch('field', function() {
|
||||
// We need to use _originalTrigger since trigger is modified by Angular Scenario.
|
||||
inputElm._originalTrigger('change');
|
||||
});
|
||||
scope.$apply();
|
||||
}
|
||||
|
||||
it('should not cause the double $digest with non isolate scopes', function() {
|
||||
run();
|
||||
});
|
||||
|
||||
it('should not cause the double $digest with isolate scopes', function() {
|
||||
scope = scope.$new(true);
|
||||
run();
|
||||
});
|
||||
scope.$apply();
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1546,6 +1557,30 @@ describe('input', function() {
|
||||
expect(scope.items[0].selected).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('password', function() {
|
||||
// Under no circumstances should input[type=password] trim inputs
|
||||
it('should not trim if ngTrim is unspecified', function() {
|
||||
compileInput('<input type="password" ng-model="password">');
|
||||
changeInputValueTo(' - - untrimmed - - ');
|
||||
expect(scope.password.length).toBe(' - - untrimmed - - '.length);
|
||||
});
|
||||
|
||||
|
||||
it('should not trim if ngTrim !== false', function() {
|
||||
compileInput('<input type="password" ng-model="password" ng-trim="true">');
|
||||
changeInputValueTo(' - - untrimmed - - ');
|
||||
expect(scope.password.length).toBe(' - - untrimmed - - '.length);
|
||||
});
|
||||
|
||||
|
||||
it('should not trim if ngTrim === false', function() {
|
||||
compileInput('<input type="password" ng-model="password" ng-trim="false">');
|
||||
changeInputValueTo(' - - untrimmed - - ');
|
||||
expect(scope.password.length).toBe(' - - untrimmed - - '.length);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('NgModel animations', function() {
|
||||
|
||||
@@ -39,4 +39,88 @@ describe('event directives', function() {
|
||||
expect($rootScope.formSubmitted).toEqual('foo');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('focus', function() {
|
||||
|
||||
describe('call the listener asynchronously during $apply', function() {
|
||||
function run(scope) {
|
||||
inject(function($compile) {
|
||||
element = $compile('<input type="text" ng-focus="focus()">')(scope);
|
||||
scope.focus = jasmine.createSpy('focus');
|
||||
|
||||
scope.$apply(function() {
|
||||
element.triggerHandler('focus');
|
||||
expect(scope.focus).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
expect(scope.focus).toHaveBeenCalledOnce();
|
||||
});
|
||||
}
|
||||
|
||||
it('should call the listener with non isolate scopes', inject(function($rootScope) {
|
||||
run($rootScope.$new());
|
||||
}));
|
||||
|
||||
it('should call the listener with isolate scopes', inject(function($rootScope) {
|
||||
run($rootScope.$new(true));
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
it('should call the listener synchronously inside of $apply if outside of $apply',
|
||||
inject(function($rootScope, $compile) {
|
||||
element = $compile('<input type="text" ng-focus="focus()" ng-model="value">')($rootScope);
|
||||
$rootScope.focus = jasmine.createSpy('focus').andCallFake(function() {
|
||||
$rootScope.value = 'newValue';
|
||||
});
|
||||
|
||||
element.triggerHandler('focus');
|
||||
|
||||
expect($rootScope.focus).toHaveBeenCalledOnce();
|
||||
expect(element.val()).toBe('newValue');
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('blur', function() {
|
||||
|
||||
describe('call the listener asynchronously during $apply', function() {
|
||||
function run(scope) {
|
||||
inject(function($compile) {
|
||||
element = $compile('<input type="text" ng-blur="blur()">')(scope);
|
||||
scope.blur = jasmine.createSpy('blur');
|
||||
|
||||
scope.$apply(function() {
|
||||
element.triggerHandler('blur');
|
||||
expect(scope.blur).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
expect(scope.blur).toHaveBeenCalledOnce();
|
||||
});
|
||||
}
|
||||
|
||||
it('should call the listener with non isolate scopes', inject(function($rootScope) {
|
||||
run($rootScope.$new());
|
||||
}));
|
||||
|
||||
it('should call the listener with isolate scopes', inject(function($rootScope) {
|
||||
run($rootScope.$new(true));
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
it('should call the listener synchronously inside of $apply if outside of $apply',
|
||||
inject(function($rootScope, $compile) {
|
||||
element = $compile('<input type="text" ng-blur="blur()" ng-model="value">')($rootScope);
|
||||
$rootScope.blur = jasmine.createSpy('blur').andCallFake(function() {
|
||||
$rootScope.value = 'newValue';
|
||||
});
|
||||
|
||||
element.triggerHandler('blur');
|
||||
|
||||
expect($rootScope.blur).toHaveBeenCalledOnce();
|
||||
expect(element.val()).toBe('newValue');
|
||||
}));
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,23 +34,5 @@ describe('ngKeyup and ngKeydown directives', function() {
|
||||
expect($rootScope.touched).toEqual(true);
|
||||
}));
|
||||
|
||||
it('should get called on focus', inject(function($rootScope, $compile) {
|
||||
element = $compile('<input ng-focus="touched = true">')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.touched).toBeFalsy();
|
||||
|
||||
browserTrigger(element, 'focus');
|
||||
expect($rootScope.touched).toEqual(true);
|
||||
}));
|
||||
|
||||
it('should get called on blur', inject(function($rootScope, $compile) {
|
||||
element = $compile('<input ng-blur="touched = true">')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.touched).toBeFalsy();
|
||||
|
||||
browserTrigger(element, 'blur');
|
||||
expect($rootScope.touched).toEqual(true);
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user