Compare commits
72 Commits
v1.2.32
...
v1.3.0-beta.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 9bfbb16e23 | |||
| 2b741dc8b8 | |||
| e888dde3c5 | |||
| d5294ebfa0 | |||
| 6d6ebf7c61 | |||
| 44b940e88d | |||
| 56e73ea355 | |||
| bfb6af7053 | |||
| d7be9588a0 | |||
| 53e4da8eab | |||
| 7b5e019981 | |||
| 129e2e021a | |||
| 3cc02e7f03 | |||
| 79592ce9e2 | |||
| a9a38d84b9 | |||
| 24a67f9515 | |||
| 91ef3a31a0 | |||
| cea44b3e86 | |||
| e8c6b9bf25 | |||
| 5412372e93 | |||
| 4f823f902d | |||
| fe0e434a87 | |||
| edad4e63df | |||
| f684cb09a5 | |||
| d7717d93e4 | |||
| 247ec19c82 | |||
| 78165c224d | |||
| d1214af132 | |||
| 11c5bb7f3d | |||
| d6419d0aff | |||
| 55848a9139 | |||
| 0f13f24ad2 | |||
| 0d8de2d3ea | |||
| 7833ce0a6e | |||
| 47ab8df455 | |||
| b700282ffd | |||
| 1b9395ea8f | |||
| 44d160e3ce | |||
| 4f90c9b531 | |||
| 11aceac273 | |||
| f08bf6f1f7 | |||
| ca4ddfadba | |||
| 4bab3d8227 | |||
| b12c6b485d | |||
| 9c353b4f17 | |||
| 21243d62a2 | |||
| ad309b1332 | |||
| 7a75356388 | |||
| dc57fe97e1 | |||
| 853999de10 | |||
| 53ec5e13e5 | |||
| 235731d32b | |||
| 5af8d2963b | |||
| 0b4a41af58 | |||
| 0e066693f2 | |||
| 02cc2b2014 | |||
| 486f1b4e51 | |||
| c5f2f583ab | |||
| 186a68f8ff | |||
| 46bd6dc88d | |||
| 0609453e1f | |||
| 7682e5747a | |||
| eaa1d00b24 | |||
| 3cf2da0e38 | |||
| 9335378602 | |||
| de2ecb8a96 | |||
| 66fdc03642 | |||
| 8e2e9adb46 | |||
| 7d604975a7 | |||
| 02075dcf13 | |||
| 7c73bc916e | |||
| 2036fb1e71 |
+2
-1
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"disallowKeywords": ["with"],
|
||||
"disallowTrailingWhitespace": true,
|
||||
"requireRightStickedOperators": ["!"]
|
||||
"requireRightStickedOperators": ["!"],
|
||||
"requireLeftStickedOperators": [","]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
|
||||
"disallowLeftStickedOperators": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||
"disallowRightStickedOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
|
||||
"requireLeftStickedOperators": [","],
|
||||
"disallowImplicitTypeConversion": ["string"],
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowKeywordsOnNewLine": ["else"],
|
||||
|
||||
@@ -1,3 +1,61 @@
|
||||
<a name="1.3.0-beta.2"></a>
|
||||
# 1.3.0-beta.2 silent-ventriloquism (2014-03-14)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$$rAF:** always fallback to a $timeout in case native rAF isn't supported
|
||||
([7b5e0199](https://github.com/angular/angular.js/commit/7b5e019981f352add88be2984de68e553d1bfa93),
|
||||
[#6654](https://github.com/angular/angular.js/issues/6654))
|
||||
- **$http:** don't convert 0 status codes to 404 for non-file protocols
|
||||
([56e73ea3](https://github.com/angular/angular.js/commit/56e73ea355c851fdfd574d6d2a9e2fcb75677945),
|
||||
[#6074](https://github.com/angular/angular.js/issues/6074), [#6155](https://github.com/angular/angular.js/issues/6155))
|
||||
- **ngAnimate:** setting classNameFilter disables animation inside ng-if
|
||||
([129e2e02](https://github.com/angular/angular.js/commit/129e2e021ab1d773874428cd1fb329eae72797c4),
|
||||
[#6539](https://github.com/angular/angular.js/issues/6539))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- whitelist blob urls for sanitization of data-bound image urls
|
||||
([47ab8df4](https://github.com/angular/angular.js/commit/47ab8df455df1f1391b760e1fbcc5c21645512b8),
|
||||
[#4623](https://github.com/angular/angular.js/issues/4623))
|
||||
|
||||
|
||||
|
||||
<a name="1.3.0-beta.1"></a>
|
||||
# 1.3.0-beta.1 retractable-eyebrow (2014-03-07)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:** support templates with thead and tfoot root elements
|
||||
([53ec5e13](https://github.com/angular/angular.js/commit/53ec5e13e5955830b6751019eef232bd2125c0b6),
|
||||
[#6289](https://github.com/angular/angular.js/issues/6289))
|
||||
- **style:** expressions in style tags
|
||||
([0609453e](https://github.com/angular/angular.js/commit/0609453e1f9ae074f8d786df903096a6eadb6aa0),
|
||||
[#2387](https://github.com/angular/angular.js/issues/2387), [#6492](https://github.com/angular/angular.js/issues/6492))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **input:** support types date, time, datetime-local, month, week
|
||||
([46bd6dc8](https://github.com/angular/angular.js/commit/46bd6dc88de252886d75426efc2ce8107a5134e9),
|
||||
[#5864](https://github.com/angular/angular.js/issues/5864))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **build:** due to [eaa1d00b](https://github.com/angular/angular.js/commit/eaa1d00b24008f590b95ad099241b4003688cdda),
|
||||
As communicated before, IE8 is no longer supported.
|
||||
- **input:** types date, time, datetime-local, month, week now always
|
||||
require a `Date` object as model ([46bd6dc8](https://github.com/angular/angular.js/commit/46bd6dc88de252886d75426efc2ce8107a5134e9),
|
||||
[#5864](https://github.com/angular/angular.js/issues/5864))
|
||||
|
||||
For more info: http://blog.angularjs.org/2013/12/angularjs-13-new-release-approaches.html
|
||||
|
||||
|
||||
|
||||
<a name="1.2.14"></a>
|
||||
# 1.2.14 feisty-cryokinesis (2014-03-01)
|
||||
|
||||
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
var files = require('./angularFiles').files;
|
||||
var util = require('./lib/grunt/utils.js');
|
||||
var versionInfo = require('./lib/versions/version-info');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
@@ -8,10 +9,10 @@ module.exports = function(grunt) {
|
||||
|
||||
grunt.loadTasks('lib/grunt');
|
||||
|
||||
var NG_VERSION = util.getVersion();
|
||||
var NG_VERSION = versionInfo.currentVersion;
|
||||
NG_VERSION.cdn = versionInfo.currentPackage.cdnVersion;
|
||||
var dist = 'angular-'+ NG_VERSION.full;
|
||||
|
||||
|
||||
//global beforeEach
|
||||
util.init();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/local/bin/node
|
||||
#!/usr/bin/env node
|
||||
|
||||
var util = require('util');
|
||||
var cp = require('child_process');
|
||||
@@ -121,9 +121,12 @@ then(function (tags) {
|
||||
value();
|
||||
}).
|
||||
then(function (tags) {
|
||||
var master = tags.pop();
|
||||
var stable = tags.pop();
|
||||
|
||||
return [
|
||||
{ name: 'v1.0.x', tag: tags[0] },
|
||||
{ name: 'master', tag: tags[1] }
|
||||
{ name: stable.replace(/\d+$/, 'x'), tag: stable },
|
||||
{ name: 'master', tag: master}
|
||||
];
|
||||
}).
|
||||
then(allInSeries(function (branch) {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<h1>Oops!</h1>
|
||||
|
||||
<p>The page you requested does not exist. Perhaps you were looking for something else...</p>
|
||||
|
||||
<div ng-controller="Error404SearchCtrl">
|
||||
|
||||
<dl ng-repeat="(key, value) in results" ng-show="value.length" style="float: left; margin-right:20px">
|
||||
<dt>{{ key }}</dt>
|
||||
<dd ng-repeat="item in value"><a ng-href="{{ item.path }}">{{ item.name }}</a></dd>
|
||||
</dl>
|
||||
</div>
|
||||
@@ -501,10 +501,6 @@ h4 {
|
||||
padding-top:20px;
|
||||
}
|
||||
|
||||
.improve-docs {
|
||||
float:right;
|
||||
}
|
||||
|
||||
.btn {
|
||||
color:#428bca;
|
||||
position: relative;
|
||||
@@ -538,10 +534,17 @@ h4 {
|
||||
background:white!important;
|
||||
}
|
||||
|
||||
.view-source, .improve-docs {
|
||||
position:relative;
|
||||
z-index:100;
|
||||
}
|
||||
|
||||
.view-source {
|
||||
margin-right:10px;
|
||||
padding-right:10px;
|
||||
border-right:1px solid #999;
|
||||
}
|
||||
|
||||
.improve-docs {
|
||||
float:right;
|
||||
}
|
||||
|
||||
.return-arguments,
|
||||
|
||||
@@ -87,7 +87,7 @@ angular.module('DocsController', [])
|
||||
breadcrumbPath += '/';
|
||||
});
|
||||
} else {
|
||||
$scope.currentArea = null;
|
||||
$scope.currentArea = NG_NAVIGATION['api'];
|
||||
$scope.breadcrumb = [];
|
||||
}
|
||||
});
|
||||
|
||||
@@ -45,6 +45,10 @@ angular.module('search', [])
|
||||
};
|
||||
}])
|
||||
|
||||
.controller('Error404SearchCtrl', ['$scope', '$location', 'docsSearch', function($scope, $location, docsSearch) {
|
||||
$scope.results = docsSearch($location.path().split(/[\/\.:]/).pop());
|
||||
}])
|
||||
|
||||
.factory('lunrSearch', function() {
|
||||
return function(properties) {
|
||||
if (window.RUNNING_IN_NG_TEST_RUNNER) return null;
|
||||
|
||||
@@ -21,10 +21,10 @@ angular.module('tutorials', [])
|
||||
element.addClass('btn-group');
|
||||
element.addClass('tutorial-nav');
|
||||
element.append(templateMerge(
|
||||
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="icon-step-backward"></i> Previous</li></a>\n' +
|
||||
'<a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="icon-play"></i> Live Demo</li></a>\n' +
|
||||
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="icon-search"></i> Code Diff</li></a>\n' +
|
||||
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="icon-step-forward"></i></li></a>', props));
|
||||
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-step-backward"></i> Previous</li></a>\n' +
|
||||
'<a href="http://angular.github.com/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="glyphicon glyphicon-play"></i> Live Demo</li></a>\n' +
|
||||
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-search"></i> Code Diff</li></a>\n' +
|
||||
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="glyphicon glyphicon-step-forward"></i></li></a>', props));
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
@@ -25,6 +25,10 @@ module.exports = function(config) {
|
||||
require('./tag-defs/tutorial-step')
|
||||
]);
|
||||
|
||||
config.append('processing.inlineTagDefinitions', [
|
||||
require('./inline-tag-defs/type')
|
||||
]);
|
||||
|
||||
config.set('processing.search.ignoreWordsFile', path.resolve(packagePath, 'ignore.words'));
|
||||
|
||||
config.prepend('rendering.templateFolders', [
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
var typeClassFilter = require('dgeni-packages/ngdoc/rendering/filters/type-class');
|
||||
var encoder = new require('node-html-encoder').Encoder();
|
||||
|
||||
module.exports = {
|
||||
name: 'type',
|
||||
description: 'Replace with markup that displays a nice type',
|
||||
handlerFactory: function() {
|
||||
return function(doc, tagName, tagDescription) {
|
||||
return '<a href="" class="' + typeClassFilter.process(tagDescription) + '">'+encoder.htmlEncode(tagDescription) + '</a>';
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -22,6 +22,12 @@ module.exports = {
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( doc.docType === 'error' ) {
|
||||
|
||||
// Parse out the error info from the id
|
||||
parts = doc.name.split(':');
|
||||
doc.namespace = parts[0];
|
||||
doc.name = parts[1];
|
||||
|
||||
|
||||
var namespaceDoc = errorNamespaces[doc.namespace];
|
||||
if ( !namespaceDoc ) {
|
||||
// First time we came across this namespace, so create a new one
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var gruntUtils = require('../../../lib/grunt/utils');
|
||||
var versionInfo = require('../../../lib/versions/version-info');
|
||||
|
||||
module.exports = {
|
||||
name: 'git-data',
|
||||
@@ -6,9 +7,9 @@ module.exports = {
|
||||
description: 'This processor adds information from the local git repository to the extraData injectable',
|
||||
init: function(config, injectables) {
|
||||
injectables.value('gitData', {
|
||||
version: gruntUtils.getVersion(),
|
||||
versions: gruntUtils.getPreviousVersions(),
|
||||
info: gruntUtils.getGitRepoInfo()
|
||||
version: versionInfo.currentVersion,
|
||||
versions: versionInfo.previousVersions,
|
||||
info: versionInfo.gitRepoInfo
|
||||
});
|
||||
},
|
||||
process: function(extraData, gitData) {
|
||||
|
||||
@@ -145,6 +145,9 @@ module.exports = {
|
||||
_(docs)
|
||||
.filter(function(doc) { return doc.area === 'api'; })
|
||||
.filter(function(doc) { return doc.docType === 'module'; })
|
||||
.forEach(function(doc) { if ( !doc.path ) {
|
||||
log.warn('Missing path property for ', doc.id);
|
||||
}})
|
||||
.map(function(doc) { return _.pick(doc, ['id', 'module', 'docType', 'area']); })
|
||||
.tap(function(docs) {
|
||||
log.debug(docs);
|
||||
@@ -188,12 +191,6 @@ module.exports = {
|
||||
area.navGroups = navGroupMapper(pages, area);
|
||||
});
|
||||
|
||||
_.forEach(docs, function(doc) {
|
||||
if ( !doc.path ) {
|
||||
log.warn('Missing path property for ', doc.id);
|
||||
}
|
||||
});
|
||||
|
||||
// Extract a list of basic page information for mapping paths to paritals and for client side searching
|
||||
var pages = _(docs)
|
||||
.map(function(doc) {
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
<div class="container main-grid main-header-grid">
|
||||
<div class="grid-left">
|
||||
<div ng-controller="DocsVersionsCtrl" class="picker version-picker">
|
||||
<select ng-options="v as ('v' + v.full) group by (v.isStable?'Stable':'Unstable') for v in docs_versions"
|
||||
<select ng-options="v as ('v' + v.version + (v.isSnapshot ? ' (snapshot)' : '')) group by (v.isStable?'Stable':'Unstable') for v in docs_versions"
|
||||
ng-model="docs_version"
|
||||
ng-change="jumpToDocsVersion(docs_version)"
|
||||
class="docs-version-jump">
|
||||
@@ -219,7 +219,7 @@
|
||||
</div>
|
||||
<div class="grid-right">
|
||||
<div id="loading" ng-show="loading">Loading...</div>
|
||||
<div ng-hide="loading" ng-include="currentPage.outputPath" onload="afterPartialLoaded()" autoscroll></div>
|
||||
<div ng-hide="loading" ng-include="currentPage.outputPath || 'Error404.html'" onload="afterPartialLoaded()" autoscroll></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -325,7 +325,7 @@ This will not render properly, unless we do some scope magic.
|
||||
|
||||
The first issue we have to solve is that the dialog box template expects `title` to be defined.
|
||||
But we would like the template's scope property `title` to be the result of interpolating the
|
||||
`<dialog>` element's `title` attribute (i.e. `"Hello {{username}}"`. Furthermore, the buttons expect
|
||||
`<dialog>` element's `title` attribute (i.e. `"Hello {{username}}"`). Furthermore, the buttons expect
|
||||
the `onOk` and `onCancel` functions to be present in the scope. This limits the usefulness of the
|
||||
widget. To solve the mapping issue we use the `locals` to create local variables which the template
|
||||
expects as follows:
|
||||
|
||||
@@ -293,9 +293,9 @@ The `restrict` option is typically set to:
|
||||
* `'E'` - only matches element name
|
||||
* `'C'` - only matches class name
|
||||
|
||||
These restictions can all be combined as needed:
|
||||
These restrictions can all be combined as needed:
|
||||
|
||||
* `'AEC'` - matches either attribure or element or class name
|
||||
* `'AEC'` - matches either attribute or element or class name
|
||||
|
||||
Let's change our directive to use `restrict: 'E'`:
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ Note that `novalidate` is used to disable browser's native form validation.
|
||||
|
||||
# Using CSS classes
|
||||
|
||||
To allow styling of form as well as controls, `ngModel` add these CSS classes:
|
||||
To allow styling of form as well as controls, `ngModel` adds these CSS classes:
|
||||
|
||||
- `ng-valid`
|
||||
- `ng-invalid`
|
||||
@@ -211,26 +211,24 @@ In the following example we create two directives.
|
||||
|
||||
<example module="form-example1">
|
||||
<file name="index.html">
|
||||
<div ng-controller="Controller">
|
||||
<form name="form" class="css-form" novalidate>
|
||||
<div>
|
||||
Size (integer 0 - 10):
|
||||
<input type="number" ng-model="size" name="size"
|
||||
min="0" max="10" integer />{{size}}<br />
|
||||
<span ng-show="form.size.$error.integer">This is not valid integer!</span>
|
||||
<span ng-show="form.size.$error.min || form.size.$error.max">
|
||||
The value must be in range 0 to 10!</span>
|
||||
</div>
|
||||
<form name="form" class="css-form" novalidate>
|
||||
<div>
|
||||
Size (integer 0 - 10):
|
||||
<input type="number" ng-model="size" name="size"
|
||||
min="0" max="10" integer />{{size}}<br />
|
||||
<span ng-show="form.size.$error.integer">This is not valid integer!</span>
|
||||
<span ng-show="form.size.$error.min || form.size.$error.max">
|
||||
The value must be in range 0 to 10!</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Length (float):
|
||||
<input type="text" ng-model="length" name="length" smart-float />
|
||||
{{length}}<br />
|
||||
<span ng-show="form.length.$error.float">
|
||||
This is not a valid float number!</span>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div>
|
||||
Length (float):
|
||||
<input type="text" ng-model="length" name="length" smart-float />
|
||||
{{length}}<br />
|
||||
<span ng-show="form.length.$error.float">
|
||||
This is not a valid float number!</span>
|
||||
</div>
|
||||
</form>
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
|
||||
@@ -7,7 +7,7 @@ AngularJS version 1.2 introduces several breaking changes that may require chang
|
||||
application's source code.
|
||||
|
||||
Although we try to avoid breaking changes, there are some cases where it is unavoidable.
|
||||
AngularJS 1.2 has undergone a thourough security review to make applications safer by default,
|
||||
AngularJS 1.2 has undergone a thorough security review to make applications safer by default,
|
||||
which has driven many of these changes. Several new features, especially animations, would not
|
||||
be possible without a few changes. Finally, some outstanding bugs were best fixed by changing
|
||||
an existing API.
|
||||
@@ -43,7 +43,7 @@ below should still apply, but you may want to consult the
|
||||
<li>{@link guide/migration#ngscenario ngScenario}</li>
|
||||
<li>{@link guide/migration#nginclude-and-ngview-replace-its-entire-element-on-update ngInclude and ngView replace its entire element on update}</li>
|
||||
<li>{@link guide/migration#urls-are-now-sanitized-against-a-whitelist URLs are now sanitized against a whitelist}</li>
|
||||
<li>{@link guide/migration#isolate-scope-only-exposed-to-directives-with-property Isolate scope only exposed to directives with <code>scope</code> property}</li>
|
||||
<li>{@link guide/migration#isolate-scope-only-exposed-to-directives-with-scope-property Isolate scope only exposed to directives with <code>scope</code> property}</li>
|
||||
<li>{@link guide/migration#change-to-interpolation-priority Change to interpolation priority}</li>
|
||||
<li>{@link guide/migration#underscore-prefixed/suffixed-properties-are-non-bindable Underscore-prefixed/suffixed properties are non-bindable}</li>
|
||||
<li>{@link guide/migration#you-cannot-bind-to-select[multiple] You cannot bind to select[multiple]}</li>
|
||||
@@ -493,7 +493,7 @@ See [31f190d4](https://github.com/angular/angular.js/commit/31f190d4d53921d32253
|
||||
|
||||
the priority of ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView has changed. This could affect directives that explicitly specify their priority.
|
||||
|
||||
In order to make ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView work together in all common scenarios their directives are being adjusted to achieve the following precendence:
|
||||
In order to make ngRepeat, ngSwitchWhen, ngIf, ngInclude and ngView work together in all common scenarios their directives are being adjusted to achieve the following precedence:
|
||||
|
||||
|
||||
Directive | Old Priority | New Priority
|
||||
@@ -532,7 +532,7 @@ See [7d69d52a](https://github.com/angular/angular.js/commit/7d69d52acff8578e0f7d
|
||||
|
||||
A whitelist configured via `$compileProvider` can be used to configure what URLs are considered safe.
|
||||
By default all common protocol prefixes are whitelisted including `data:` URIs with mime types `image/*`.
|
||||
This change sholdn't impact apps that don't contain malicious image links.
|
||||
This change shouldn't impact apps that don't contain malicious image links.
|
||||
|
||||
See [1adf29af](https://github.com/angular/angular.js/commit/1adf29af13890d61286840177607edd552a9df97),
|
||||
[3e39ac7e](https://github.com/angular/angular.js/commit/3e39ac7e1b10d4812a44dad2f959a93361cd823b).
|
||||
@@ -613,7 +613,7 @@ controller.) That's easier said that done for two reasons:
|
||||
someone on the scope chain for JavaScript use, you also expose it to
|
||||
Angular expressions
|
||||
2. The new `controller as` syntax that's now in increased usage exposes the
|
||||
entire controller on the scope chain greatly increaing the exposed surface.
|
||||
entire controller on the scope chain greatly increasing the exposed surface.
|
||||
|
||||
Though Angular expressions are written and controlled by the developer, they:
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ primitive, object literal, function, or even an instance of a custom type.
|
||||
## Service Recipe
|
||||
|
||||
JavaScript developers often use custom types to write object-oriented code. Let's explore how we
|
||||
could launch a unicorn into the space via our `unicornLauncher` service that is an instance of
|
||||
could launch a unicorn into space via our `unicornLauncher` service that is an instance of
|
||||
custom type:
|
||||
|
||||
```javascript
|
||||
|
||||
@@ -11,15 +11,15 @@ Angular services are:
|
||||
|
||||
* Lazily instantiated – Angular only instantiates a service when an application component depends
|
||||
on it.
|
||||
* Singletons – Each component is dependent on a service gets a reference to the single instance
|
||||
* Singletons – Each component dependent on a service gets a reference to the single instance
|
||||
generated by the service factory.
|
||||
|
||||
Angular offers several useful services (like {@link ng.$http `$http`}) but for most applications
|
||||
Angular offers several useful services (like {@link ng.$http `$http`}), but for most applications
|
||||
you'll also want to {@link services#creating-services create your own}.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Note:** Like other core Angular identifiers built-in services always start with `$`
|
||||
(i.e. `$http`).
|
||||
(e.g. `$http`).
|
||||
</div>
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ injection of `$window`, `$scope`, and our `notify` service:
|
||||
</file>
|
||||
</example>
|
||||
|
||||
<div class="alert alert-error">
|
||||
<div class="alert alert-danger">
|
||||
**Careful:** If you plan to [minify](http://en.wikipedia.org/wiki/Minification_(programming) your code,
|
||||
your variable names will get renamed unless you use one of the annotation techniques above.
|
||||
</div>
|
||||
@@ -215,8 +215,8 @@ In the example, note that:
|
||||
{@link ng.$log `$log`} services.
|
||||
* The `routeTemplateMonitor` service depends on the built-in {@link ngRoute.$route `$route`}
|
||||
service and our custom `batchLog` service.
|
||||
* Both services use the and array notation to declare their dependencies.
|
||||
* That the order of identifiers in the array is the same as the order of argument
|
||||
* Both services use the array notation to declare their dependencies.
|
||||
* The order of identifiers in the array is the same as the order of argument
|
||||
names in the factory function.
|
||||
|
||||
### Registering a Service with `$provide`
|
||||
@@ -234,7 +234,7 @@ angular.module('myModule', []).config(function($provide) {
|
||||
});
|
||||
```
|
||||
|
||||
This is technique is often used in unit tests to mock out a service's dependencies.
|
||||
This technique is often used in unit tests to mock out a service's dependencies.
|
||||
|
||||
|
||||
## Unit Testing
|
||||
|
||||
@@ -258,7 +258,7 @@ expect($scope.strength).toEqual('weak');
|
||||
```
|
||||
|
||||
Notice that the test is not only much shorter, it is also easier to follow what is happening. We say
|
||||
that such a test tells a story, rather then asserting random bits which don't seem to be related.
|
||||
that such a test tells a story, rather than asserting random bits which don't seem to be related.
|
||||
|
||||
## Filters
|
||||
{@link ng.$filterProvider Filters} are functions which transform the data into a user readable
|
||||
|
||||
@@ -9,7 +9,8 @@ the construction of an AngularJS web app. The app you will build is a catalog th
|
||||
of Android devices, lets you filter the list to see only devices that interest you, and then view
|
||||
details for any device.
|
||||
|
||||
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413">
|
||||
<img class="diagram" src="img/tutorial/catalog_screen.png" width="488" height="413" alt="demo
|
||||
application running in the browser">
|
||||
|
||||
Work through the tutorial to see how Angular makes browsers smarter — without the use of extensions
|
||||
or plug-ins. As you work through the tutorial, you will:
|
||||
@@ -57,63 +58,71 @@ and follow the instructions for setting up your computer.
|
||||
<div class="tab-pane well" id="git-mac" title="Git on Mac/Linux">
|
||||
<ol>
|
||||
<li><p>You'll need Git, which you can get from
|
||||
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
|
||||
<a href="http://git-scm.com/download" title="Git site download">the Git site</a>.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at
|
||||
<a href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<a href="https://github.com/angular/angular-phonecat" title="Github Phonecat Repo">Github</a> by
|
||||
running the following command:</p>
|
||||
<pre>git clone https://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the <code>angular-phonecat</code> directory in your current
|
||||
directory.</p></li>
|
||||
<p>This command creates the <code>angular-phonecat</code> directory in your current directory.</p></li>
|
||||
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions, from now on, assume you are running all commands from the <code>angular-phonecat</code>
|
||||
directory.</p></li>
|
||||
<p>The tutorial instructions, from now on, assume you are running all commands from the
|
||||
<code>angular-phonecat</code> directory.</p></li>
|
||||
<li><p>You will also need Node.js and Karma to run unit tests, so please verify that you have
|
||||
<a href="http://nodejs.org/">Node.js</a> v0.10 or better installed
|
||||
<a href="http://nodejs.org/" title="NodeJS org">Node.js</a> v0.10 or better installed
|
||||
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
|
||||
command in a terminal window:</p></li>
|
||||
<pre>node --version</pre>
|
||||
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> and its plugins if you
|
||||
don't have it already:</p>
|
||||
<div class="alert alert-info">**Helpful note:** If you need to run a different version of
|
||||
node.js in your local environment, consider installing
|
||||
<a href="https://github.com/creationix/nvm" title="Node Version Manager Github Repo link">
|
||||
Node Version Manager (nvm)</a>.</div>
|
||||
<p>Additionally install <a href="http://karma-runner.github.io/" title="Karma site">Karma</a> and
|
||||
its plugins if you don't have it already:</p>
|
||||
<pre>
|
||||
npm install
|
||||
</pre></li>
|
||||
<li><p>You will need an http server running on your system. Mac and Linux machines typically
|
||||
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
|
||||
to run a simple bundled http server: <code>node scripts/web-server.js</code>.</p></li>
|
||||
have Apache pre-installed, but If you don't already have one installed, you can use <code>node</code>
|
||||
to run <code>scripts/web-server.js</code>, a simple bundled http server.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane well" id="git-win" title="Git on Windows">
|
||||
<ol>
|
||||
<li><p>You will need Node.js and Karma to run unit tests, so please verify that you have
|
||||
<a href="http://nodejs.org/">Node.js</a> v0.10 or better installed
|
||||
<a href="http://nodejs.org/" title="NodeJS site">Node.js</a> v0.10 or better installed
|
||||
and that the <code>node</code> executable is on your <code>PATH</code> by running the following
|
||||
command in a terminal window:</p>
|
||||
<pre>node --version</pre>
|
||||
<p>Additionally install <a href="http://karma-runner.github.io/">Karma</a> if you
|
||||
don't have it already:</p>
|
||||
<div class="alert alert-info">**Helpful note:** If you need to run a different version of
|
||||
node.js in your local environment, consider installing
|
||||
<a href="https://github.com/creationix/nvm" title="Node Version Manager Github Repo link">
|
||||
Node Version Manager (nvm)</a>.</div>
|
||||
<p>Additionally install <a href="http://karma-runner.github.io/" title="Karma site">Karma</a>
|
||||
if you don't have it already:</p>
|
||||
<pre>npm install -g karma</pre>
|
||||
</li>
|
||||
<li><p>You'll also need Git, which you can get from
|
||||
<a href="http://git-scm.com/download">the Git site</a>.</p></li>
|
||||
<a href="http://git-scm.com/download" title="Git site download">the Git site</a>.</p></li>
|
||||
<li><p>Clone the angular-phonecat repository located at <a
|
||||
href="https://github.com/angular/angular-phonecat">Github</a> by running the following command:</p>
|
||||
<pre>git clone https://github.com/angular/angular-phonecat.git</pre>
|
||||
href="https://github.com/angular/angular-phonecat" "Github Angular-phonecat Repo">Github</a> by running
|
||||
the following command:</p><pre>git clone https://github.com/angular/angular-phonecat.git</pre>
|
||||
<p>This command creates the <code>angular-phonecat</code> directory in your current directory.</p></li>
|
||||
<li><p>Change your current directory to <code>angular-phonecat</code>:</p>
|
||||
<pre>cd angular-phonecat</pre>
|
||||
<p>The tutorial instructions assume you are running all commands from the <code>angular-phonecat</code>
|
||||
directory.</p>
|
||||
directory.</p>
|
||||
<p>You should run all <code>git</code> commands from Git bash.</p>
|
||||
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be
|
||||
executed from the Windows command line.</li>
|
||||
<li><p>You need an http server running on your system, but if you don't already have one
|
||||
already installed, you can use <code>node</code> to run a simple
|
||||
bundled http server: <code>node scripts\web-server.js</code>.</p></li>
|
||||
<p>Other commands like <code>test.bat</code> or <code>e2e-test.bat</code> should be executed from the
|
||||
Windows command line.</li>
|
||||
<li><p>You need an http server running on your system, but if you don't already have one already
|
||||
installed, you can use <code>node</code> to run <code>scripts\web-server.js</code>, a simple bundled
|
||||
http server.</p></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
The last thing to do is to make sure your computer has a web browser and a good text editor
|
||||
installed. Now, let's get some cool stuff done!
|
||||
The last thing to do is to make sure your computer has a web browser and a good text editor installed. Now,
|
||||
let's get some cool stuff done!
|
||||
|
||||
{@link step_00 <span class="btn btn-primary">Get Started!</span>}
|
||||
<a href="tutorial/step_00" title="Next Step"><span class="btn btn-primary">Get Started!</span></a>
|
||||
|
||||
@@ -183,7 +183,7 @@ is available to be injected.
|
||||
### Writing and Running Tests
|
||||
Angular developers prefer the syntax of Jasmine's Behavior-driven Development (BDD) framework when
|
||||
writing tests. Although Angular does not require you to use Jasmine, we wrote all of the tests in
|
||||
this tutorial in Jasmine. You can learn about Jasmine on the [Jasmine home page](http://pivotal.github.com/jasmine/) and at the [Jasmine docs](http://pivotal.github.io/jasmine/).
|
||||
this tutorial in Jasmine. You can learn about Jasmine on the [Jasmine home page](http://jasmine.github.io/) and at the [Jasmine docs](http://jasmine.github.io/).
|
||||
|
||||
The angular-seed project is pre-configured to run all unit tests using [Karma](http://karma-runner.github.io/). Ensure that the necessary karma plugins are installed.
|
||||
You can do this by issuing `npm install` into your terminal.
|
||||
|
||||
@@ -107,7 +107,7 @@ __`app/partials/phone-detail.html`:__
|
||||
</dl>
|
||||
</li>
|
||||
...
|
||||
</li>
|
||||
<li>
|
||||
<span>Additional Features</span>
|
||||
<dd>{{phone.additionalFeatures}}</dd>
|
||||
</li>
|
||||
|
||||
@@ -40,13 +40,13 @@ The name of our filter is "checkmark". The `input` evaluates to either `true` or
|
||||
return one of the two unicode characters we have chosen to represent true (`\u2713` -> ✓) or false (`\u2718` -> ✘).
|
||||
|
||||
Now that our filter is ready, we need to register the `phonecatFilters` module as a dependency for
|
||||
our main `phonecat` module.
|
||||
our main `phonecatApp` module.
|
||||
|
||||
__`app/js/app.js`:__
|
||||
|
||||
```js
|
||||
...
|
||||
angular.module('phonecatApp', ['phonecatFilters']).
|
||||
angular.module('phonecatApp', ['ngRoute','phonecatControllers','phonecatFilters']).
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ __`app/js/app.js`.__
|
||||
|
||||
```js
|
||||
// ...
|
||||
angular.module('phonecat', [
|
||||
angular.module('phonecatApp', [
|
||||
'ngRoute',
|
||||
|
||||
'phonecatAnimations',
|
||||
|
||||
+9
-7
@@ -1,13 +1,12 @@
|
||||
var path = require('canonical-path');
|
||||
var gruntUtils = require('../lib/grunt/utils');
|
||||
var versionInfo = require('../lib/versions/version-info');
|
||||
var basePath = __dirname;
|
||||
|
||||
var basePackage = require('./config');
|
||||
|
||||
module.exports = function(config) {
|
||||
|
||||
var version = gruntUtils.getVersion();
|
||||
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + version.cdn;
|
||||
var cdnUrl = "//ajax.googleapis.com/ajax/libs/angularjs/" + versionInfo.currentPackage.cdnVersion;
|
||||
|
||||
var getVersion = function(component, sourceFolder, packageFile) {
|
||||
sourceFolder = sourceFolder || '../bower_components';
|
||||
@@ -25,9 +24,12 @@ module.exports = function(config) {
|
||||
{ pattern: '**/*.ngdoc', basePath: path.resolve(basePath, 'content') }
|
||||
]);
|
||||
|
||||
config.set('processing.stopOnError', true);
|
||||
|
||||
config.set('processing.errors.minerrInfoPath', path.resolve(basePath, '../build/errors.json'));
|
||||
|
||||
config.set('rendering.outputFolder', '../build/docs');
|
||||
config.set('rendering.contentsFolder', 'partials');
|
||||
|
||||
config.set('logging.level', 'info');
|
||||
|
||||
@@ -38,7 +40,7 @@ module.exports = function(config) {
|
||||
commonFiles: {
|
||||
scripts: [ '../../../angular.js' ]
|
||||
},
|
||||
dependencyPath: '../../..'
|
||||
dependencyPath: '../../../'
|
||||
},
|
||||
scripts: [
|
||||
'../angular.js',
|
||||
@@ -73,7 +75,7 @@ module.exports = function(config) {
|
||||
commonFiles: {
|
||||
scripts: [ '../../../angular.min.js' ]
|
||||
},
|
||||
dependencyPath: '../../..'
|
||||
dependencyPath: '../../../'
|
||||
},
|
||||
scripts: [
|
||||
'../angular.min.js',
|
||||
@@ -111,7 +113,7 @@ module.exports = function(config) {
|
||||
'../../../angular.js'
|
||||
]
|
||||
},
|
||||
dependencyPath: '../../..'
|
||||
dependencyPath: '../../../'
|
||||
},
|
||||
scripts: [
|
||||
'components/jquery-' + getVersion('jquery') + '/jquery.js',
|
||||
@@ -147,7 +149,7 @@ module.exports = function(config) {
|
||||
commonFiles: {
|
||||
scripts: [ cdnUrl + '/angular.min.js' ]
|
||||
},
|
||||
dependencyPath: cdnUrl
|
||||
dependencyPath: cdnUrl + '/'
|
||||
},
|
||||
scripts: [
|
||||
cdnUrl + '/angular.min.js',
|
||||
|
||||
+5
-1
@@ -49,7 +49,11 @@ gulp.task('assets', ['bower'], function() {
|
||||
|
||||
|
||||
gulp.task('doc-gen', function() {
|
||||
return docGenerator('docs.config.js').generateDocs();
|
||||
return docGenerator('docs.config.js')
|
||||
.generateDocs()
|
||||
.catch(function(error) {
|
||||
process.exit(1);
|
||||
});
|
||||
});
|
||||
|
||||
// JSHint the example and protractor test files
|
||||
|
||||
+2
-2
@@ -7,10 +7,10 @@ echo "#################################"
|
||||
# Enable tracing and exit on first failure
|
||||
set -xe
|
||||
|
||||
# Define reasonable set of browsers in case we are running manually from commandline
|
||||
# This is the default set of browsers to use on the CI server unless overridden via env variable
|
||||
if [[ -z "$BROWSERS" ]]
|
||||
then
|
||||
BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh,/Users/jenkins/bin/ie8.sh,/Users/jenkins/bin/ie9.sh"
|
||||
BROWSERS="Chrome,Firefox,Opera,/Users/jenkins/bin/safari.sh"
|
||||
fi
|
||||
|
||||
# CLEAN #
|
||||
|
||||
@@ -45,12 +45,6 @@ module.exports = function(config, specificOptions) {
|
||||
platform: 'OS X 10.9',
|
||||
version: '7'
|
||||
},
|
||||
'SL_IE_8': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 7',
|
||||
version: '8'
|
||||
},
|
||||
'SL_IE_9': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
@@ -88,13 +82,6 @@ module.exports = function(config, specificOptions) {
|
||||
os: 'Windows',
|
||||
os_version: '8'
|
||||
},
|
||||
'BS_IE_8': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
browser_version: '8.0',
|
||||
os: 'Windows',
|
||||
os_version: '7'
|
||||
},
|
||||
'BS_IE_9': {
|
||||
base: 'BrowserStack',
|
||||
browser: 'ie',
|
||||
|
||||
+4
-174
@@ -4,8 +4,9 @@ var shell = require('shelljs');
|
||||
var grunt = require('grunt');
|
||||
var spawn = require('child_process').spawn;
|
||||
var semver = require('semver');
|
||||
|
||||
var _ = require('lodash');
|
||||
var version, pkg;
|
||||
|
||||
var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n';
|
||||
|
||||
var PORT_MIN = 8000;
|
||||
@@ -23,23 +24,6 @@ var getRandomPorts = function() {
|
||||
];
|
||||
};
|
||||
|
||||
var getPackage = function() {
|
||||
if ( !pkg ) {
|
||||
|
||||
// Search up the folder hierarchy for the first package.json
|
||||
var packageFolder = path.resolve('.');
|
||||
while ( !fs.existsSync(path.join(packageFolder, 'package.json')) ) {
|
||||
var parent = path.dirname(packageFolder);
|
||||
if ( parent === packageFolder) { break; }
|
||||
packageFolder = parent;
|
||||
}
|
||||
pkg = JSON.parse(fs.readFileSync(path.join(packageFolder,'package.json'), 'UTF-8'));
|
||||
|
||||
}
|
||||
|
||||
return pkg;
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -50,160 +34,6 @@ module.exports = {
|
||||
},
|
||||
|
||||
|
||||
getGitRepoInfo: function() {
|
||||
var GITURL_REGEX = /^https:\/\/github.com\/([^\/]+)\/(.+).git$/;
|
||||
var match = GITURL_REGEX.exec(getPackage().repository.url);
|
||||
var git = {
|
||||
owner: match[1],
|
||||
repo: match[2]
|
||||
};
|
||||
return git;
|
||||
},
|
||||
|
||||
|
||||
getVersion: function(){
|
||||
if (version) return version;
|
||||
|
||||
try {
|
||||
|
||||
var gitTag = getTagOfCurrentCommit();
|
||||
var semVerVersion, codeName, fullVersion;
|
||||
if (gitTag) {
|
||||
// tagged release
|
||||
fullVersion = semVerVersion = semver.valid(gitTag);
|
||||
codeName = getTaggedReleaseCodeName(gitTag);
|
||||
} else {
|
||||
// snapshot release
|
||||
semVerVersion = getSnapshotVersion();
|
||||
fullVersion = semVerVersion + '-' + getSnapshotSuffix();
|
||||
codeName = 'snapshot';
|
||||
}
|
||||
|
||||
var versionParts = semVerVersion.match(/(\d+)\.(\d+)\.(\d+)/);
|
||||
|
||||
version = {
|
||||
full: fullVersion,
|
||||
major: versionParts[1],
|
||||
minor: versionParts[2],
|
||||
dot: versionParts[3],
|
||||
codename: codeName,
|
||||
cdn: getPackage().cdnVersion
|
||||
};
|
||||
|
||||
// Stable versions have an even minor version
|
||||
version.isStable = version.minor%2 === 0;
|
||||
|
||||
return version;
|
||||
|
||||
} catch (e) {
|
||||
grunt.fail.warn(e);
|
||||
}
|
||||
|
||||
function getTagOfCurrentCommit() {
|
||||
var gitTagResult = shell.exec('git describe --exact-match', {silent:true});
|
||||
var gitTagOutput = gitTagResult.output.trim();
|
||||
var branchVersionPattern = new RegExp(getPackage().branchVersion.replace('.', '\\.').replace('*', '\\d+'));
|
||||
if (gitTagResult.code === 0 && gitTagOutput.match(branchVersionPattern)) {
|
||||
return gitTagOutput;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getTaggedReleaseCodeName(tagName) {
|
||||
var tagMessage = shell.exec('git cat-file -p '+ tagName +' | grep "codename"', {silent:true}).output;
|
||||
var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
|
||||
if (!codeName) {
|
||||
throw new Error("Could not extract release code name. The message of tag "+tagName+
|
||||
" must match '*codename(some release name)*'");
|
||||
}
|
||||
return codeName;
|
||||
}
|
||||
|
||||
function getSnapshotVersion() {
|
||||
var oldTags = shell.exec('git tag -l v'+getPackage().branchVersion, {silent:true}).output.trim().split('\n');
|
||||
// ignore non semver versions.
|
||||
oldTags = oldTags.filter(function(version) {
|
||||
return version && semver.valid(version);
|
||||
});
|
||||
if (oldTags.length) {
|
||||
oldTags.sort(semver.compare);
|
||||
semVerVersion = oldTags[oldTags.length-1];
|
||||
if (semVerVersion.indexOf('-') !== -1) {
|
||||
semVerVersion = semver.inc(semVerVersion, 'prerelease');
|
||||
} else {
|
||||
semVerVersion = semver.inc(semVerVersion, 'patch');
|
||||
}
|
||||
} else {
|
||||
semVerVersion = semver.valid(getPackage().branchVersion.replace(/\*/g, '0'));
|
||||
}
|
||||
return semVerVersion;
|
||||
}
|
||||
|
||||
function getSnapshotSuffix() {
|
||||
var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER || 'local';
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
return 'build.'+jenkinsBuild+'+sha.'+hash;
|
||||
}
|
||||
},
|
||||
|
||||
getPreviousVersions: function() {
|
||||
var VERSION_REGEX = /([1-9]\d*)\.(\d+)\.(\d+)(?:-?rc\.?(\d+)|-(snapshot))?/;
|
||||
|
||||
// Pad out a number with zeros at the front to make it `digits` characters long
|
||||
function pad(num, digits) {
|
||||
var zeros = Array(digits+1).join('0');
|
||||
return (zeros+num).slice(-digits);
|
||||
}
|
||||
|
||||
function padVersion(version) {
|
||||
// We pad out the version numbers with 0s so they sort nicely
|
||||
// - Non-Release Candidates get 9999 for their release candidate section to make them appear earlier
|
||||
// - Snapshots get 9 added to the front to move them to the top of the list
|
||||
var maxLength = 4;
|
||||
var padded = (version.snapshot ? '9' : '0') + pad(version.major, maxLength) +
|
||||
pad(version.minor, maxLength) + pad(version.dot, maxLength) +
|
||||
pad(version.rc || 9999, maxLength);
|
||||
return padded;
|
||||
}
|
||||
|
||||
function getVersionFromTag(tag) {
|
||||
var match = VERSION_REGEX.exec(tag);
|
||||
if ( match ) {
|
||||
var version = {
|
||||
tag: tag,
|
||||
major: match[1], minor: match[2], dot: match[3], rc: match[4],
|
||||
snapshot: !!match[5] && getSnapshotSuffix()
|
||||
};
|
||||
|
||||
if(version.snapshot) {
|
||||
version.full = version.major + '.' + version.minor + '.x (edge)';
|
||||
} else {
|
||||
version.full = version.major + '.' + version.minor + '.' + version.dot +
|
||||
(version.rc ? '-rc.' + version.rc : '');
|
||||
}
|
||||
|
||||
// Stable versions have an even minor version and are not a release candidate
|
||||
version.isStable = !(version.minor%2 || version.rc);
|
||||
|
||||
// Versions before 1.0.2 had a different docs folder name
|
||||
version.docsUrl = 'http://code.angularjs.org/' + version.full + '/docs';
|
||||
if ( version.major < 1 || (version.major === 1 && version.minor === 0 && version.dot < 2 ) ) {
|
||||
version.docsUrl += '-' + version.full;
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
var tags = shell.exec('git tag', {silent: true}).output.split(/\s*\n\s*/);
|
||||
return _(tags)
|
||||
.map(getVersionFromTag)
|
||||
.filter() // getVersion can map to undefined - this clears those out
|
||||
.sortBy(padVersion)
|
||||
.value();
|
||||
},
|
||||
|
||||
startKarma: function(config, singleRun, done){
|
||||
var browsers = grunt.option('browsers');
|
||||
var reporters = grunt.option('reporters');
|
||||
@@ -225,7 +55,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
|
||||
updateWebdriver: function(done){
|
||||
updateWebdriver: function(done){
|
||||
if (process.env.TRAVIS) {
|
||||
// Skip the webdriver-manager update on Travis, since the browsers will
|
||||
// be provided remotely.
|
||||
@@ -374,7 +204,7 @@ module.exports = {
|
||||
var mapFile = minFile + '.map';
|
||||
var mapFileName = mapFile.match(/[^\/]+$/)[0];
|
||||
var errorFileName = file.replace(/\.js$/, '-errors.json');
|
||||
var versionNumber = this.getVersion().full;
|
||||
var versionNumber = grunt.config('NG_VERSION').full;
|
||||
shell.exec(
|
||||
'java ' +
|
||||
this.java32flags() + ' ' +
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var shell = require('shelljs');
|
||||
var semver = require('semver');
|
||||
var _ = require('lodash');
|
||||
|
||||
var currentPackage, previousVersions;
|
||||
|
||||
|
||||
/**
|
||||
* Load information about this project from the package.json
|
||||
* @return {Object} The package information
|
||||
*/
|
||||
var getPackage = function() {
|
||||
// Search up the folder hierarchy for the first package.json
|
||||
var packageFolder = path.resolve('.');
|
||||
while ( !fs.existsSync(path.join(packageFolder, 'package.json')) ) {
|
||||
var parent = path.dirname(packageFolder);
|
||||
if ( parent === packageFolder) { break; }
|
||||
packageFolder = parent;
|
||||
}
|
||||
return JSON.parse(fs.readFileSync(path.join(packageFolder,'package.json'), 'UTF-8'));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Parse the github URL for useful information
|
||||
* @return {Object} An object containing the github owner and repository name
|
||||
*/
|
||||
var getGitRepoInfo = function() {
|
||||
var GITURL_REGEX = /^https:\/\/github.com\/([^\/]+)\/(.+).git$/;
|
||||
var match = GITURL_REGEX.exec(currentPackage.repository.url);
|
||||
var git = {
|
||||
owner: match[1],
|
||||
repo: match[2]
|
||||
};
|
||||
return git;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Extract the code name from the tagged commit's message - it should contain the text of the form:
|
||||
* "codename(some-code-name)"
|
||||
* @param {String} tagName Name of the tag to look in for the codename
|
||||
* @return {String} The codename if found, otherwise null/undefined
|
||||
*/
|
||||
var getCodeName = function(tagName) {
|
||||
var tagMessage = shell.exec('git cat-file -p '+ tagName +' | grep "codename"', {silent:true}).output;
|
||||
var codeName = tagMessage && tagMessage.match(/codename\((.*)\)/)[1];
|
||||
if (!codeName) {
|
||||
throw new Error("Could not extract release code name. The message of tag "+tagName+
|
||||
" must match '*codename(some release name)*'");
|
||||
}
|
||||
return codeName;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Compute a build segment for the version, from the Jenkins build number and current commit SHA
|
||||
* @return {String} The build segment of the version
|
||||
*/
|
||||
function getBuild() {
|
||||
var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', '');
|
||||
return 'sha.'+hash;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* If the current commit is tagged as a version get that version
|
||||
* @return {SemVer} The version or null
|
||||
*/
|
||||
var getTaggedVersion = function() {
|
||||
var gitTagResult = shell.exec('git describe --exact-match', {silent:true});
|
||||
|
||||
if ( gitTagResult.code === 0 ) {
|
||||
var tag = gitTagResult.output.trim();
|
||||
var version = semver.parse(tag);
|
||||
|
||||
if ( version && semver.satisfies(version, currentPackage.branchVersion)) {
|
||||
version.codeName = getCodeName(tag);
|
||||
version.full = version.version;
|
||||
return version;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stable versions have an even minor version and have no prerelease
|
||||
* @param {SemVer} version The version to test
|
||||
* @return {Boolean} True if the version is stable
|
||||
*/
|
||||
var isStable = function(version) {
|
||||
return semver.satisfies(version, '1.0 || 1.2') && version.prerelease.length === 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a collection of all the previous versions sorted by semantic version
|
||||
* @return {Array.<SemVer>} The collection of previous versions
|
||||
*/
|
||||
var getPreviousVersions = function() {
|
||||
// always use the remote tags as the local clone might
|
||||
// not contain all commits when cloned with git clone --depth=...
|
||||
// Needed e.g. for Travis
|
||||
var tagResults = shell.exec('git ls-remote --tags | grep -o -e "v[0-9].*[0-9]$"', {silent: true});
|
||||
if ( tagResults.code === 0 ) {
|
||||
return _(tagResults.output.trim().split('\n'))
|
||||
.map(function(tag) {
|
||||
var version = semver.parse(tag);
|
||||
return version;
|
||||
})
|
||||
.filter()
|
||||
.map(function(version) {
|
||||
version.isStable = isStable(version);
|
||||
|
||||
version.docsUrl = 'http://code.angularjs.org/' + version.version + '/docs';
|
||||
// Versions before 1.0.2 had a different docs folder name
|
||||
if ( version.major < 1 || (version.major === 1 && version.minor === 0 && version.dot < 2 ) ) {
|
||||
version.docsUrl += '-' + version.version;
|
||||
}
|
||||
return version;
|
||||
})
|
||||
.sort(semver.compare)
|
||||
.value();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the unstable snapshot version
|
||||
* @return {SemVer} The snapshot version
|
||||
*/
|
||||
var getSnapshotVersion = function() {
|
||||
version = _(previousVersions)
|
||||
.filter(function(tag) {
|
||||
return semver.satisfies(tag, currentPackage.branchVersion);
|
||||
})
|
||||
.last();
|
||||
|
||||
if ( !version ) {
|
||||
// a snapshot version before the first tag on the branch
|
||||
version = semver(currentPackage.branchVersion.replace('*','0-beta.1'));
|
||||
}
|
||||
|
||||
// We need to clone to ensure that we are not modifying another version
|
||||
version = semver(version.raw);
|
||||
|
||||
var jenkinsBuild = process.env.TRAVIS_BUILD_NUMBER || process.env.BUILD_NUMBER;
|
||||
if (!version.prerelease || !version.prerelease.length) {
|
||||
// last release was a non beta release. Increment the patch level to
|
||||
// indicate the next release that we will be doing.
|
||||
// E.g. last release was 1.3.0, then the snapshot will be
|
||||
// 1.3.1-build.1, which is lesser than 1.3.1 accorind the semver!
|
||||
|
||||
// If the last release was a beta release we don't update the
|
||||
// beta number by purpose, as otherwise the semver comparison
|
||||
// does not work any more when the next beta is released.
|
||||
// E.g. don't generate 1.3.0-beta.2.build.1
|
||||
// as this is bigger than 1.3.0-beta.2 according to semver
|
||||
version.patch++;
|
||||
}
|
||||
version.prerelease = jenkinsBuild ? ['build', jenkinsBuild] : ['local'];
|
||||
version.build = getBuild();
|
||||
version.codeName = 'snapshot';
|
||||
version.isSnapshot = true;
|
||||
version.format();
|
||||
version.full = version.version + '+' + version.build;
|
||||
|
||||
return version;
|
||||
};
|
||||
|
||||
|
||||
exports.currentPackage = currentPackage = getPackage();
|
||||
exports.previousVersions = previousVersions = getPreviousVersions();
|
||||
exports.currentVersion = getTaggedVersion() || getSnapshotVersion();
|
||||
exports.gitRepoInfo = getGitRepoInfo();
|
||||
Generated
+4314
File diff suppressed because it is too large
Load Diff
+7
-6
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "angularjs",
|
||||
"branchVersion": "1.2.*",
|
||||
"cdnVersion": "1.2.14",
|
||||
"branchVersion": "1.3.*",
|
||||
"cdnVersion": "1.3.0-beta.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/angular/angular.js.git"
|
||||
@@ -16,7 +16,7 @@
|
||||
"grunt-contrib-jshint": "~0.7.2",
|
||||
"grunt-ddescribe-iit": "~0.0.1",
|
||||
"grunt-jasmine-node": "git://github.com/vojtajina/grunt-jasmine-node.git#fix-grunt-exit-code",
|
||||
"grunt-jscs-checker": "~0.3.2",
|
||||
"grunt-jscs-checker": "~0.4.0",
|
||||
"grunt-merge-conflict": "~0.0.1",
|
||||
"grunt-parallel": "~0.3.1",
|
||||
"grunt-shell": "~0.4.0",
|
||||
@@ -50,10 +50,11 @@
|
||||
"gulp-concat": "~2.1.7",
|
||||
"canonical-path": "0.0.2",
|
||||
"winston": "~0.7.2",
|
||||
"dgeni": "~0.2.0",
|
||||
"dgeni-packages": "^0.3.0",
|
||||
"dgeni": "^0.2.2",
|
||||
"dgeni-packages": "^0.7.0",
|
||||
"gulp-jshint": "~1.4.2",
|
||||
"jshint-stylish": "~0.1.5"
|
||||
"jshint-stylish": "~0.1.5",
|
||||
"node-html-encoder": "0.0.2"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
|
||||
@@ -55,19 +55,37 @@ function prepare {
|
||||
git commit -m "v$NEW_VERSION"
|
||||
}
|
||||
|
||||
function publish {
|
||||
if [[ $IS_SNAPSHOT_BUILD ]]; then
|
||||
echo "-- Updating snapshot version"
|
||||
curl -G --data-urlencode "ver=$NEW_VERSION" http://code.angularjs.org/fetchLatestSnapshot.php
|
||||
exit 0;
|
||||
fi
|
||||
|
||||
function _update_snapshot() {
|
||||
for backend in "$@" ; do
|
||||
echo "-- Updating snapshot version: backend=$backend"
|
||||
curl -G --data-urlencode "ver=$NEW_VERSION" http://$backend:8003/fetchLatestSnapshot.php
|
||||
done
|
||||
}
|
||||
|
||||
function _update_code() {
|
||||
cd $REPO_DIR
|
||||
|
||||
echo "-- Pushing code.angularjs.org"
|
||||
git push origin master
|
||||
|
||||
echo "-- Refreshing code.angularjs.org"
|
||||
curl http://code.angularjs.org/gitFetchSite.php
|
||||
for backend in "$@" ; do
|
||||
echo "-- Refreshing code.angularjs.org: backend=$backend"
|
||||
curl http://$backend:8003/gitFetchSite.php
|
||||
done
|
||||
}
|
||||
|
||||
function publish {
|
||||
# The TXT record for backends.angularjs.org is a CSV of the IP addresses for
|
||||
# the currently serving Compute Engine backends.
|
||||
# code.angularjs.org is served out of port 8003 on these backends.
|
||||
backends=("$(dig backends.angularjs.org +short TXT | python -c 'print raw_input()[1:-1].replace(",", "\n")')")
|
||||
|
||||
if [[ $IS_SNAPSHOT_BUILD ]]; then
|
||||
_update_snapshot ${backends[@]}
|
||||
else
|
||||
_update_code ${backends[@]}
|
||||
fi
|
||||
}
|
||||
|
||||
source $(dirname $0)/../utils.inc
|
||||
|
||||
@@ -23,7 +23,7 @@ ARG_DEFS=(
|
||||
)
|
||||
|
||||
function init {
|
||||
if [[ $(git rev-parse --short HEAD) != $COMMIT_SHA ]]; then
|
||||
if [[ $(git rev-parse HEAD) != $(git rev-parse $COMMIT_SHA) ]]; then
|
||||
echo "HEAD is not at $COMMIT_SHA"
|
||||
usage
|
||||
fi
|
||||
|
||||
@@ -7,8 +7,8 @@ export SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||
if [ $JOB = "unit" ]; then
|
||||
grunt ci-checks
|
||||
grunt test:promises-aplus
|
||||
grunt test:unit --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_8,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
|
||||
grunt test:docs --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_8,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
|
||||
grunt test:unit --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
|
||||
grunt test:docs --browsers SL_Chrome,SL_Safari,SL_Firefox,SL_IE_9,SL_IE_10,SL_IE_11 --reporters dots
|
||||
elif [ $JOB = "e2e" ]; then
|
||||
export TARGET_SPECS="build/docs/ptore2e/**/*jqlite_test.js"
|
||||
if [ $TEST_TARGET = "jquery" ]; then
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@
|
||||
# 0. Set the current directory to the directory of the script. By this
|
||||
# the script can be called from anywhere.
|
||||
# 1. Parse the named arguments
|
||||
# 2. If the parameter "git_push_dryrun" is set, all calls the `git push` in this script
|
||||
# 2. If the parameter "git_push_dryrun" is set, all calls to `git push` in this script
|
||||
# or in child scripts will be intercepted so that the `--dry-run` and `--porcelain` is added
|
||||
# to show what the push would do but not actually do it.
|
||||
# 3. If the parameter "verbose" is set, the `-x` flag will be set in bash.
|
||||
@@ -36,7 +36,7 @@
|
||||
# with the name of the parameter in upper case (with dash converted to underscore).
|
||||
#
|
||||
# Special arguments that are always available:
|
||||
# - "--action=.*": This parameter will be used to dispatch to a function with that name when the
|
||||
# - "--action=.*": This parameter will be used to execute a function with that name when the
|
||||
# script is started
|
||||
# - "--git_push_dryrun=true": This will intercept all calls to `git push` in this script
|
||||
# or in child scripts so that the `--dry-run` and `--porcelain` is added
|
||||
@@ -195,7 +195,7 @@ function isFunction {
|
||||
}
|
||||
|
||||
# readJsonProp(jsonFile, property)
|
||||
# - restriction: property needs to be on an own line!
|
||||
# - restriction: property needs to be on a single line!
|
||||
function readJsonProp {
|
||||
echo $(sed -En 's/.*"'$2'"[ ]*:[ ]*"(.*)".*/\1/p' $1)
|
||||
}
|
||||
|
||||
@@ -101,6 +101,9 @@
|
||||
"getter": false,
|
||||
"getBlockElements": false,
|
||||
|
||||
/* filters.js */
|
||||
"getFirstThursdayOfYear": false,
|
||||
|
||||
/* AngularPublic.js */
|
||||
"version": false,
|
||||
"publishExternalAPI": false,
|
||||
|
||||
+1
-1
@@ -457,7 +457,7 @@ forEach({
|
||||
return jqLite(element).data('$isolateScope') || jqLite(element).data('$isolateScopeNoTemplate');
|
||||
},
|
||||
|
||||
controller: jqLiteController ,
|
||||
controller: jqLiteController,
|
||||
|
||||
injector: function(element) {
|
||||
return jqLiteInheritedData(element, '$injector');
|
||||
|
||||
@@ -114,7 +114,6 @@ function setupModuleLoader(window) {
|
||||
* @ngdoc property
|
||||
* @name angular.Module#requires
|
||||
* @module ng
|
||||
* @propertyOf angular.Module
|
||||
* @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
|
||||
@@ -126,7 +125,6 @@ function setupModuleLoader(window) {
|
||||
* @ngdoc property
|
||||
* @name angular.Module#name
|
||||
* @module ng
|
||||
* @propertyOf angular.Module
|
||||
* @returns {string} Name of the module.
|
||||
* @description
|
||||
*/
|
||||
|
||||
+1
-1
@@ -194,7 +194,6 @@ function Browser(window, document, $log, $sniffer) {
|
||||
|
||||
/**
|
||||
* @name $browser#onUrlChange
|
||||
* @TODO(vojta): refactor to use node's syntax for events
|
||||
*
|
||||
* @description
|
||||
* Register callback function that will be called, when url changes.
|
||||
@@ -215,6 +214,7 @@ function Browser(window, document, $log, $sniffer) {
|
||||
* @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
|
||||
*/
|
||||
self.onUrlChange = function(callback) {
|
||||
// TODO(vojta): refactor to use node's syntax for events
|
||||
if (!urlChangeInit) {
|
||||
// We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
|
||||
// don't fire popstate when user change the address bar and don't fire hashchange when url
|
||||
|
||||
+8
-9
@@ -503,7 +503,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
Suffix = 'Directive',
|
||||
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
|
||||
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
|
||||
TABLE_CONTENT_REGEXP = /^<\s*(tr|th|td|tbody)(\s+[^>]*)?>/i;
|
||||
TABLE_CONTENT_REGEXP = /^<\s*(tr|th|td|thead|tbody|tfoot)(\s+[^>]*)?>/i;
|
||||
|
||||
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
|
||||
// The assumption is that future DOM event attribute names will begin with
|
||||
@@ -1649,16 +1649,15 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
|
||||
template = trim(template);
|
||||
if ((type = TABLE_CONTENT_REGEXP.exec(template))) {
|
||||
type = type[1].toLowerCase();
|
||||
var table = jqLite('<table>' + template + '</table>'),
|
||||
tbody = table.children('tbody'),
|
||||
leaf = /(td|th)/.test(type) && table.find('tr');
|
||||
if (tbody.length && type !== 'tbody') {
|
||||
table = tbody;
|
||||
var table = jqLite('<table>' + template + '</table>');
|
||||
if (/(thead|tbody|tfoot)/.test(type)) {
|
||||
return table.children(type);
|
||||
}
|
||||
if (leaf && leaf.length) {
|
||||
table = leaf;
|
||||
table = table.children('tbody');
|
||||
if (type === 'tr') {
|
||||
return table.children('tr');
|
||||
}
|
||||
return table.contents();
|
||||
return table.children('tr').contents();
|
||||
}
|
||||
return jqLite('<div>' +
|
||||
template +
|
||||
|
||||
+533
-2
@@ -11,6 +11,11 @@
|
||||
var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
|
||||
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+/=?^_`{|}~.-]+@[a-z0-9-]+(\.[a-z0-9-]+)*$/i;
|
||||
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
|
||||
var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
|
||||
var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)$/;
|
||||
var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
|
||||
var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
|
||||
var TIME_REGEXP = /^(\d\d):(\d\d)$/;
|
||||
|
||||
var inputType = {
|
||||
|
||||
@@ -91,6 +96,425 @@ var inputType = {
|
||||
*/
|
||||
'text': textInputType,
|
||||
|
||||
/**
|
||||
* @ngdoc input
|
||||
* @name input[date]
|
||||
*
|
||||
* @description
|
||||
* Input with date validation and transformation. In browsers that do not yet support
|
||||
* the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
|
||||
* date format (yyyy-MM-dd), for example: `2009-01-06`. The model must always be a Date object.
|
||||
*
|
||||
* @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=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
|
||||
* valid ISO date string (yyyy-MM-dd).
|
||||
* @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
|
||||
* a valid ISO date string (yyyy-MM-dd).
|
||||
* @param {string=} required Sets `required` validation error key if the value is not entered.
|
||||
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
|
||||
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
|
||||
* `required` when you want to data-bind to the `required` attribute.
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
*
|
||||
* @example
|
||||
<example name="date-input-directive">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.value = new Date(2013, 9, 22);
|
||||
}
|
||||
</script>
|
||||
<form name="myForm" ng-controller="Ctrl as dateCtrl">
|
||||
Pick a date between in 2013:
|
||||
<input type="date" id="exampleInput" name="input" ng-model="value"
|
||||
placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
|
||||
<span class="error" ng-show="myForm.input.$error.required">
|
||||
Required!</span>
|
||||
<span class="error" ng-show="myForm.input.$error.date">
|
||||
Not a valid date!</span>
|
||||
<tt>value = {{value | date: "yyyy-MM-dd"}}</tt><br/>
|
||||
<tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
|
||||
<tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
|
||||
<tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
|
||||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
||||
</form>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
var value = element(by.binding('value | date: "yyyy-MM-dd"'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var input = element(by.model('value'));
|
||||
|
||||
// currently protractor/webdriver does not support
|
||||
// sending keys to all known HTML5 input controls
|
||||
// for various browsers (see https://github.com/angular/protractor/issues/562).
|
||||
function setInput(val) {
|
||||
// set the value of the element and force validation.
|
||||
var scr = "var ipt = document.getElementById('exampleInput'); " +
|
||||
"ipt.value = '" + val + "';" +
|
||||
"angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
|
||||
browser.executeScript(scr);
|
||||
}
|
||||
|
||||
it('should initialize to model', function() {
|
||||
expect(value.getText()).toContain('2013-10-22');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
setInput('');
|
||||
expect(value.getText()).toEqual('value =');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = false');
|
||||
});
|
||||
|
||||
it('should be invalid if over max', function() {
|
||||
setInput('2015-01-01');
|
||||
expect(value.getText()).toContain('');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = false');
|
||||
});
|
||||
</file>
|
||||
</example>f
|
||||
*/
|
||||
'date': createDateInputType('date', DATE_REGEXP,
|
||||
createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
|
||||
'yyyy-MM-dd'),
|
||||
|
||||
/**
|
||||
* @ngdoc input
|
||||
* @name input[dateTimeLocal]
|
||||
*
|
||||
* @description
|
||||
* Input with datetime validation and transformation. In browsers that do not yet support
|
||||
* the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
|
||||
* local datetime format (yyyy-MM-ddTHH:mm), for example: `2010-12-28T14:57`. The model must be a Date object.
|
||||
*
|
||||
* @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=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
|
||||
* valid ISO datetime format (yyyy-MM-ddTHH:mm).
|
||||
* @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
|
||||
* a valid ISO datetime format (yyyy-MM-ddTHH:mm).
|
||||
* @param {string=} required Sets `required` validation error key if the value is not entered.
|
||||
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
|
||||
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
|
||||
* `required` when you want to data-bind to the `required` attribute.
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
*
|
||||
* @example
|
||||
<example name="datetimelocal-input-directive">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.value = new Date(2010, 11, 28, 14, 57);
|
||||
}
|
||||
</script>
|
||||
<form name="myForm" ng-controller="Ctrl as dateCtrl">
|
||||
Pick a date between in 2013:
|
||||
<input type="datetime-local" id="exampleInput" name="input" ng-model="value"
|
||||
placeholder="yyyy-MM-ddTHH:mm" min="2001-01-01T00:00" max="2013-12-31T00:00" required />
|
||||
<span class="error" ng-show="myForm.input.$error.required">
|
||||
Required!</span>
|
||||
<span class="error" ng-show="myForm.input.$error.datetimelocal">
|
||||
Not a valid date!</span>
|
||||
<tt>value = {{value | date: "yyyy-MM-ddTHH:mm"}}</tt><br/>
|
||||
<tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
|
||||
<tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
|
||||
<tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
|
||||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
||||
</form>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
var value = element(by.binding('value | date: "yyyy-MM-ddTHH:mm"'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var input = element(by.model('value'));
|
||||
|
||||
// currently protractor/webdriver does not support
|
||||
// sending keys to all known HTML5 input controls
|
||||
// for various browsers (https://github.com/angular/protractor/issues/562).
|
||||
function setInput(val) {
|
||||
// set the value of the element and force validation.
|
||||
var scr = "var ipt = document.getElementById('exampleInput'); " +
|
||||
"ipt.value = '" + val + "';" +
|
||||
"angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
|
||||
browser.executeScript(scr);
|
||||
}
|
||||
|
||||
it('should initialize to model', function() {
|
||||
expect(value.getText()).toContain('2010-12-28T14:57');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
setInput('');
|
||||
expect(value.getText()).toEqual('value =');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = false');
|
||||
});
|
||||
|
||||
it('should be invalid if over max', function() {
|
||||
setInput('2015-01-01T23:59');
|
||||
expect(value.getText()).toContain('');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = false');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
|
||||
createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm']),
|
||||
'yyyy-MM-ddTHH:mm'),
|
||||
|
||||
/**
|
||||
* @ngdoc input
|
||||
* @name input[time]
|
||||
*
|
||||
* @description
|
||||
* Input with time validation and transformation. In browsers that do not yet support
|
||||
* the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
|
||||
* local time format (HH:mm), for example: `14:57`. Model must be a Date object. This binding will always output a
|
||||
* Date object to the model of January 1, 1900, or local date `new Date(0, 0, 1, HH, mm)`.
|
||||
*
|
||||
* @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=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
|
||||
* valid ISO time format (HH:mm).
|
||||
* @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be a
|
||||
* valid ISO time format (HH:mm).
|
||||
* @param {string=} required Sets `required` validation error key if the value is not entered.
|
||||
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
|
||||
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
|
||||
* `required` when you want to data-bind to the `required` attribute.
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
*
|
||||
* @example
|
||||
<example name="time-input-directive">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.value = new Date(0, 0, 1, 14, 57);
|
||||
}
|
||||
</script>
|
||||
<form name="myForm" ng-controller="Ctrl as dateCtrl">
|
||||
Pick a between 8am and 5pm:
|
||||
<input type="time" id="exampleInput" name="input" ng-model="value"
|
||||
placeholder="HH:mm" min="08:00" max="17:00" required />
|
||||
<span class="error" ng-show="myForm.input.$error.required">
|
||||
Required!</span>
|
||||
<span class="error" ng-show="myForm.input.$error.time">
|
||||
Not a valid date!</span>
|
||||
<tt>value = {{value | date: "HH:mm"}}</tt><br/>
|
||||
<tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
|
||||
<tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
|
||||
<tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
|
||||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
||||
</form>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
var value = element(by.binding('value | date: "HH:mm"'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var input = element(by.model('value'));
|
||||
|
||||
// currently protractor/webdriver does not support
|
||||
// sending keys to all known HTML5 input controls
|
||||
// for various browsers (https://github.com/angular/protractor/issues/562).
|
||||
function setInput(val) {
|
||||
// set the value of the element and force validation.
|
||||
var scr = "var ipt = document.getElementById('exampleInput'); " +
|
||||
"ipt.value = '" + val + "';" +
|
||||
"angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
|
||||
browser.executeScript(scr);
|
||||
}
|
||||
|
||||
it('should initialize to model', function() {
|
||||
expect(value.getText()).toContain('14:57');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
setInput('');
|
||||
expect(value.getText()).toEqual('value =');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = false');
|
||||
});
|
||||
|
||||
it('should be invalid if over max', function() {
|
||||
setInput('23:59');
|
||||
expect(value.getText()).toContain('');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = false');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
'time': createDateInputType('time', TIME_REGEXP,
|
||||
createDateParser(TIME_REGEXP, ['HH', 'mm']),
|
||||
'HH:mm'),
|
||||
|
||||
/**
|
||||
* @ngdoc input
|
||||
* @name input[week]
|
||||
*
|
||||
* @description
|
||||
* Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
|
||||
* the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
|
||||
* week format (yyyy-W##), for example: `2013-W02`. The model must always be a Date object.
|
||||
*
|
||||
* @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=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
|
||||
* valid ISO week format (yyyy-W##).
|
||||
* @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
|
||||
* a valid ISO week format (yyyy-W##).
|
||||
* @param {string=} required Sets `required` validation error key if the value is not entered.
|
||||
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
|
||||
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
|
||||
* `required` when you want to data-bind to the `required` attribute.
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
*
|
||||
* @example
|
||||
<example name="week-input-directive">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.value = new Date(2013, 0, 3);
|
||||
}
|
||||
</script>
|
||||
<form name="myForm" ng-controller="Ctrl as dateCtrl">
|
||||
Pick a date between in 2013:
|
||||
<input id="exampleInput" type="week" name="input" ng-model="value"
|
||||
placeholder="YYYY-W##" min="2012-W32" max="2013-W52" required />
|
||||
<span class="error" ng-show="myForm.input.$error.required">
|
||||
Required!</span>
|
||||
<span class="error" ng-show="myForm.input.$error.week">
|
||||
Not a valid date!</span>
|
||||
<tt>value = {{value | date: "yyyy-Www"}}</tt><br/>
|
||||
<tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
|
||||
<tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
|
||||
<tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
|
||||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
||||
</form>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
var value = element(by.binding('value | date: "yyyy-Www"'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var input = element(by.model('value'));
|
||||
|
||||
// currently protractor/webdriver does not support
|
||||
// sending keys to all known HTML5 input controls
|
||||
// for various browsers (https://github.com/angular/protractor/issues/562).
|
||||
function setInput(val) {
|
||||
// set the value of the element and force validation.
|
||||
var scr = "var ipt = document.getElementById('exampleInput'); " +
|
||||
"ipt.value = '" + val + "';" +
|
||||
"angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
|
||||
browser.executeScript(scr);
|
||||
}
|
||||
|
||||
it('should initialize to model', function() {
|
||||
expect(value.getText()).toContain('2013-W01');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
setInput('');
|
||||
expect(value.getText()).toEqual('value =');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = false');
|
||||
});
|
||||
|
||||
it('should be invalid if over max', function() {
|
||||
setInput('2015-W01');
|
||||
expect(value.getText()).toContain('');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = false');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
|
||||
|
||||
/**
|
||||
* @ngdoc input
|
||||
* @name input[month]
|
||||
*
|
||||
* @description
|
||||
* Input with month validation and transformation. In browsers that do not yet support
|
||||
* the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
|
||||
* month format (yyyy-MM), for example: `2009-01`. The model must always be a Date object. In the event the model is
|
||||
* not set to the first of the month, the first of that model's month is assumed.
|
||||
*
|
||||
* @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=} min Sets the `min` validation error key if the value entered is less than `min`. This must be
|
||||
* a valid ISO month format (yyyy-MM).
|
||||
* @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must
|
||||
* be a valid ISO month format (yyyy-MM).
|
||||
* @param {string=} required Sets `required` validation error key if the value is not entered.
|
||||
* @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
|
||||
* the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
|
||||
* `required` when you want to data-bind to the `required` attribute.
|
||||
* @param {string=} ngChange Angular expression to be executed when input changes due to user
|
||||
* interaction with the input element.
|
||||
*
|
||||
* @example
|
||||
<example name="month-input-directive">
|
||||
<file name="index.html">
|
||||
<script>
|
||||
function Ctrl($scope) {
|
||||
$scope.value = new Date(2013, 9, 1);
|
||||
}
|
||||
</script>
|
||||
<form name="myForm" ng-controller="Ctrl as dateCtrl">
|
||||
Pick a month int 2013:
|
||||
<input id="exampleInput" type="month" name="input" ng-model="value"
|
||||
placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
|
||||
<span class="error" ng-show="myForm.input.$error.required">
|
||||
Required!</span>
|
||||
<span class="error" ng-show="myForm.input.$error.month">
|
||||
Not a valid month!</span>
|
||||
<tt>value = {{value | date: "yyyy-MM"}}</tt><br/>
|
||||
<tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
|
||||
<tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
|
||||
<tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
|
||||
<tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
|
||||
</form>
|
||||
</file>
|
||||
<file name="protractor.js" type="protractor">
|
||||
var value = element(by.binding('value | date: "yyyy-MM"'));
|
||||
var valid = element(by.binding('myForm.input.$valid'));
|
||||
var input = element(by.model('value'));
|
||||
|
||||
// currently protractor/webdriver does not support
|
||||
// sending keys to all known HTML5 input controls
|
||||
// for various browsers (https://github.com/angular/protractor/issues/562).
|
||||
function setInput(val) {
|
||||
// set the value of the element and force validation.
|
||||
var scr = "var ipt = document.getElementById('exampleInput'); " +
|
||||
"ipt.value = '" + val + "';" +
|
||||
"angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
|
||||
browser.executeScript(scr);
|
||||
}
|
||||
|
||||
it('should initialize to model', function() {
|
||||
expect(value.getText()).toContain('2013-10');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = true');
|
||||
});
|
||||
|
||||
it('should be invalid if empty', function() {
|
||||
setInput('');
|
||||
expect(value.getText()).toEqual('value =');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = false');
|
||||
});
|
||||
|
||||
it('should be invalid if over max', function() {
|
||||
setInput('2015-01');
|
||||
expect(value.getText()).toContain('');
|
||||
expect(valid.getText()).toContain('myForm.input.$valid = false');
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
'month': createDateInputType('month', MONTH_REGEXP,
|
||||
createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
|
||||
'yyyy-MM'),
|
||||
|
||||
/**
|
||||
* @ngdoc input
|
||||
@@ -593,6 +1017,108 @@ function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
}
|
||||
}
|
||||
|
||||
function weekParser(isoWeek) {
|
||||
if(isDate(isoWeek)) {
|
||||
return isoWeek;
|
||||
}
|
||||
|
||||
if(isString(isoWeek)) {
|
||||
WEEK_REGEXP.lastIndex = 0;
|
||||
var parts = WEEK_REGEXP.exec(isoWeek);
|
||||
if(parts) {
|
||||
var year = +parts[1],
|
||||
week = +parts[2],
|
||||
firstThurs = getFirstThursdayOfYear(year),
|
||||
addDays = (week - 1) * 7;
|
||||
return new Date(year, 0, firstThurs.getDate() + addDays);
|
||||
}
|
||||
}
|
||||
|
||||
return NaN;
|
||||
}
|
||||
|
||||
function createDateParser(regexp, mapping) {
|
||||
return function(iso) {
|
||||
var parts, map;
|
||||
|
||||
if(isDate(iso)) {
|
||||
return iso;
|
||||
}
|
||||
|
||||
if(isString(iso)) {
|
||||
regexp.lastIndex = 0;
|
||||
parts = regexp.exec(iso);
|
||||
|
||||
if(parts) {
|
||||
parts.shift();
|
||||
map = { yyyy: 0, MM: 1, dd: 1, HH: 0, mm: 0 };
|
||||
|
||||
forEach(parts, function(part, index) {
|
||||
if(index < mapping.length) {
|
||||
map[mapping[index]] = +part;
|
||||
}
|
||||
});
|
||||
|
||||
return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm);
|
||||
}
|
||||
}
|
||||
|
||||
return NaN;
|
||||
};
|
||||
}
|
||||
|
||||
function createDateInputType(type, regexp, parseDate, format) {
|
||||
return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
|
||||
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
||||
|
||||
ctrl.$parsers.push(function(value) {
|
||||
if(ctrl.$isEmpty(value)) {
|
||||
ctrl.$setValidity(type, true);
|
||||
return null;
|
||||
}
|
||||
|
||||
if(regexp.test(value)) {
|
||||
ctrl.$setValidity(type, true);
|
||||
return parseDate(value);
|
||||
}
|
||||
|
||||
ctrl.$setValidity(type, false);
|
||||
return undefined;
|
||||
});
|
||||
|
||||
ctrl.$formatters.push(function(value) {
|
||||
if(isDate(value)) {
|
||||
return $filter('date')(value, format);
|
||||
}
|
||||
return '';
|
||||
});
|
||||
|
||||
if(attr.min) {
|
||||
var minValidator = function(value) {
|
||||
var valid = ctrl.$isEmpty(value) ||
|
||||
(parseDate(value) >= parseDate(attr.min));
|
||||
ctrl.$setValidity('min', valid);
|
||||
return valid ? value : undefined;
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(minValidator);
|
||||
ctrl.$formatters.push(minValidator);
|
||||
}
|
||||
|
||||
if(attr.max) {
|
||||
var maxValidator = function(value) {
|
||||
var valid = ctrl.$isEmpty(value) ||
|
||||
(parseDate(value) <= parseDate(attr.max));
|
||||
ctrl.$setValidity('max', valid);
|
||||
return valid ? value : undefined;
|
||||
};
|
||||
|
||||
ctrl.$parsers.push(maxValidator);
|
||||
ctrl.$formatters.push(maxValidator);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
|
||||
textInputType(scope, element, attr, ctrl, $sniffer, $browser);
|
||||
|
||||
@@ -852,14 +1378,14 @@ function checkboxInputType(scope, element, attr, ctrl) {
|
||||
</file>
|
||||
</example>
|
||||
*/
|
||||
var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) {
|
||||
var inputDirective = ['$browser', '$sniffer', '$filter', function($browser, $sniffer, $filter) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
require: '?ngModel',
|
||||
link: function(scope, element, attr, ctrl) {
|
||||
if (ctrl) {
|
||||
(inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer,
|
||||
$browser);
|
||||
$browser, $filter);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1247,6 +1773,11 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
|
||||
* - {@link input[number] number}
|
||||
* - {@link input[email] email}
|
||||
* - {@link input[url] url}
|
||||
* - {@link input[date] date}
|
||||
* - {@link input[dateTimeLocal] dateTimeLocal}
|
||||
* - {@link input[time] time}
|
||||
* - {@link input[month] month}
|
||||
* - {@link input[week] week}
|
||||
* - {@link ng.directive:select select}
|
||||
* - {@link ng.directive:textarea textarea}
|
||||
*
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
|
||||
* `{{ expression }}` which is similar but less verbose.
|
||||
*
|
||||
* It is preferrable to use `ngBind` instead of `{{ expression }}` when a template is momentarily
|
||||
* It is preferable to use `ngBind` instead of `{{ expression }}` when a template is momentarily
|
||||
* displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
|
||||
* element attribute, it makes the bindings invisible to the user while the page is loading.
|
||||
*
|
||||
|
||||
@@ -146,7 +146,6 @@
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngInclude#$includeContentRequested
|
||||
* @eventOf ng.directive:ngInclude
|
||||
* @eventType emit on the scope ngInclude was declared in
|
||||
* @description
|
||||
* Emitted every time the ngInclude content is requested.
|
||||
@@ -156,7 +155,6 @@
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name ngInclude#$includeContentLoaded
|
||||
* @eventOf ng.directive:ngInclude
|
||||
* @eventType emit on the current ngInclude scope
|
||||
* @description
|
||||
* Emitted every time the ngInclude content is reloaded.
|
||||
|
||||
@@ -68,9 +68,11 @@
|
||||
* as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
|
||||
*
|
||||
* @animations
|
||||
* enter - when a new item is added to the list or when an item is revealed after a filter
|
||||
* leave - when an item is removed from the list or when an item is filtered out
|
||||
* move - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
|
||||
* **.enter** - when a new item is added to the list or when an item is revealed after a filter
|
||||
*
|
||||
* **.leave** - when an item is removed from the list or when an item is filtered out
|
||||
*
|
||||
* **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
|
||||
*
|
||||
* @element ANY
|
||||
* @scope
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
* @scope
|
||||
* @priority 800
|
||||
* @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
|
||||
* @paramDescription
|
||||
* On child elements add:
|
||||
*
|
||||
* * `ngSwitchWhen`: the case statement to match against. If match then this
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
|
||||
var styleDirective = valueFn({
|
||||
restrict: 'E',
|
||||
terminal: true
|
||||
terminal: false
|
||||
});
|
||||
|
||||
@@ -235,6 +235,32 @@ function timeZoneGetter(date) {
|
||||
return paddedZone;
|
||||
}
|
||||
|
||||
function getFirstThursdayOfYear(year) {
|
||||
// 0 = index of January
|
||||
var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
|
||||
// 4 = index of Thursday (+1 to account for 1st = 5)
|
||||
// 11 = index of *next* Thursday (+1 account for 1st = 12)
|
||||
return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
|
||||
}
|
||||
|
||||
function getThursdayThisWeek(datetime) {
|
||||
return new Date(datetime.getFullYear(), datetime.getMonth(),
|
||||
// 4 = index of Thursday
|
||||
datetime.getDate() + (4 - datetime.getDay()));
|
||||
}
|
||||
|
||||
function weekGetter(size) {
|
||||
return function(date) {
|
||||
var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
|
||||
thisThurs = getThursdayThisWeek(date);
|
||||
|
||||
var diff = +thisThurs - +firstThurs,
|
||||
result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
|
||||
|
||||
return padNumber(result, size);
|
||||
};
|
||||
}
|
||||
|
||||
function ampmGetter(date, formats) {
|
||||
return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
|
||||
}
|
||||
@@ -263,10 +289,12 @@ var DATE_FORMATS = {
|
||||
EEEE: dateStrGetter('Day'),
|
||||
EEE: dateStrGetter('Day', true),
|
||||
a: ampmGetter,
|
||||
Z: timeZoneGetter
|
||||
Z: timeZoneGetter,
|
||||
ww: weekGetter(2),
|
||||
w: weekGetter(1)
|
||||
};
|
||||
|
||||
var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,
|
||||
var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|w+))(.*)/,
|
||||
NUMBER_STRING = /^\-?\d+$/;
|
||||
|
||||
/**
|
||||
@@ -301,6 +329,8 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+
|
||||
* * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
|
||||
* * `'a'`: am/pm marker
|
||||
* * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
|
||||
* * `'ww'`: ISO-8601 week of year (00-53)
|
||||
* * `'w'`: ISO-8601 week of year (0-53)
|
||||
*
|
||||
* `format` string can also be one of the following predefined
|
||||
* {@link guide/i18n localizable formats}:
|
||||
@@ -441,7 +471,7 @@ function dateFilter($locale) {
|
||||
* @returns {string} JSON string.
|
||||
*
|
||||
*
|
||||
* @example:
|
||||
* @example
|
||||
<example>
|
||||
<file name="index.html">
|
||||
<pre>{{ {'name':'value'} | json }}</pre>
|
||||
|
||||
@@ -144,9 +144,11 @@ function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDoc
|
||||
jsonpDone = xhr = null;
|
||||
|
||||
// fix status code when it is 0 (0 status is undocumented).
|
||||
// Occurs when accessing file resources.
|
||||
// On Android 4.1 stock browser it occurs while retrieving files from application cache.
|
||||
status = (status === 0) ? (response ? 200 : 404) : status;
|
||||
// Occurs when accessing file resources or on Android 4.1 stock browser
|
||||
// while retrieving files from application cache.
|
||||
if (status === 0) {
|
||||
status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
|
||||
}
|
||||
|
||||
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
|
||||
status = status == 1223 ? 204 : status;
|
||||
|
||||
+20
-10
@@ -1,21 +1,31 @@
|
||||
'use strict';
|
||||
|
||||
function $$RAFProvider(){ //rAF
|
||||
this.$get = ['$window', function($window) {
|
||||
this.$get = ['$window', '$timeout', function($window, $timeout) {
|
||||
var requestAnimationFrame = $window.requestAnimationFrame ||
|
||||
$window.webkitRequestAnimationFrame;
|
||||
$window.webkitRequestAnimationFrame ||
|
||||
$window.mozRequestAnimationFrame;
|
||||
|
||||
var cancelAnimationFrame = $window.cancelAnimationFrame ||
|
||||
$window.webkitCancelAnimationFrame;
|
||||
$window.webkitCancelAnimationFrame ||
|
||||
$window.mozCancelAnimationFrame;
|
||||
|
||||
var raf = function(fn) {
|
||||
var id = requestAnimationFrame(fn);
|
||||
return function() {
|
||||
cancelAnimationFrame(id);
|
||||
};
|
||||
};
|
||||
var rafSupported = !!requestAnimationFrame;
|
||||
var raf = rafSupported
|
||||
? function(fn) {
|
||||
var id = requestAnimationFrame(fn);
|
||||
return function() {
|
||||
cancelAnimationFrame(id);
|
||||
};
|
||||
}
|
||||
: function(fn) {
|
||||
var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
|
||||
return function() {
|
||||
$timeout.cancel(timer);
|
||||
};
|
||||
};
|
||||
|
||||
raf.supported = !!requestAnimationFrame;
|
||||
raf.supported = rafSupported;
|
||||
|
||||
return raf;
|
||||
}];
|
||||
|
||||
@@ -139,7 +139,6 @@ function $RootScopeProvider(){
|
||||
/**
|
||||
* @ngdoc property
|
||||
* @name $rootScope.Scope#$id
|
||||
* @propertyOf ng.$rootScope.Scope
|
||||
* @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for
|
||||
* debugging.
|
||||
*/
|
||||
@@ -652,7 +651,6 @@ function $RootScopeProvider(){
|
||||
/**
|
||||
* @ngdoc event
|
||||
* @name $rootScope.Scope#$destroy
|
||||
* @eventOf ng.$rootScope.Scope
|
||||
* @eventType broadcast on scope being destroyed
|
||||
*
|
||||
* @description
|
||||
|
||||
@@ -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|blob):|data:image\//;
|
||||
|
||||
/**
|
||||
* @description
|
||||
|
||||
+1
-1
@@ -521,7 +521,7 @@ function $SceDelegateProvider() {
|
||||
* |---------------------|----------------|
|
||||
* | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. |
|
||||
* | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
|
||||
* | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't consititute an SCE context. |
|
||||
* | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. |
|
||||
* | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contens are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
|
||||
* | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. |
|
||||
*
|
||||
|
||||
@@ -701,8 +701,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
/**
|
||||
*
|
||||
* @ngdoc function
|
||||
* @name ng.$animate#setClass
|
||||
* @methodOf ng.$animate
|
||||
* @name $animate#setClass
|
||||
* @function
|
||||
* @description Adds and/or removes the given CSS classes to and from the element.
|
||||
* Once complete, the done() callback will be fired (if provided).
|
||||
@@ -771,7 +770,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
fireDOMOperation();
|
||||
fireBeforeCallbackAsync();
|
||||
fireAfterCallbackAsync();
|
||||
fireDoneCallbackAsync();
|
||||
closeAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -950,7 +949,7 @@ angular.module('ngAnimate', ['ng'])
|
||||
animation, but class-based animations don't. An example of this
|
||||
failing would be when a parent HTML tag has a ng-class attribute
|
||||
causing ALL directives below to skip animations during the digest */
|
||||
if(runner.isClassBased) {
|
||||
if(runner && runner.isClassBased) {
|
||||
cleanup(element, className);
|
||||
} else {
|
||||
$$asyncCallback(function() {
|
||||
|
||||
Vendored
+2
-2
@@ -1821,8 +1821,8 @@ angular.module('ngMockE2E', ['ng']).config(['$provide', function($provide) {
|
||||
* an array containing response status (number), response data (string) and response headers
|
||||
* (Object).
|
||||
* - passThrough – `{function()}` – Any request matching a backend definition with `passThrough`
|
||||
* handler, will be pass through to the real backend (an XHR request will be made to the
|
||||
* server.
|
||||
* handler will be passed through to the real backend (an XHR request will be made to the
|
||||
* server.)
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -253,7 +253,7 @@ function shallowClearAndCopy(src, dst) {
|
||||
|
||||
```js
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
var user = User.get({userId:123}, function() {
|
||||
User.get({userId:123}, function(user) {
|
||||
user.abc = true;
|
||||
user.$save();
|
||||
});
|
||||
@@ -273,6 +273,16 @@ function shallowClearAndCopy(src, dst) {
|
||||
});
|
||||
});
|
||||
```
|
||||
*
|
||||
* You can also access the raw `$http` promise via the `$promise` property on the object returned
|
||||
*
|
||||
```
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
User.get({userId:123})
|
||||
.$promise.then(function(user) {
|
||||
$scope.user = user;
|
||||
});
|
||||
```
|
||||
|
||||
* # Creating a custom 'PUT' request
|
||||
* In this example we create a custom method on our resource to make a PUT request
|
||||
|
||||
+100
-99
@@ -82,7 +82,7 @@ function $RouteProvider(){
|
||||
*
|
||||
* If `template` is a function, it will be called with the following parameters:
|
||||
*
|
||||
* - `{Array.<Object>}` - route parameters extracted from the current
|
||||
* - `{Array.<Object>}` - route parameters extracted from the current
|
||||
* `$location.path()` by applying the current route
|
||||
*
|
||||
* - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
|
||||
@@ -90,7 +90,7 @@ function $RouteProvider(){
|
||||
*
|
||||
* If `templateUrl` is a function, it will be called with the following parameters:
|
||||
*
|
||||
* - `{Array.<Object>}` - route parameters extracted from the current
|
||||
* - `{Array.<Object>}` - route parameters extracted from the current
|
||||
* `$location.path()` by applying the current route
|
||||
*
|
||||
* - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
|
||||
@@ -247,7 +247,7 @@ function $RouteProvider(){
|
||||
* - `$scope` - The current route scope.
|
||||
* - `$template` - The current route template HTML.
|
||||
*
|
||||
* @property {Array.<Object>} routes Array of all configured routes.
|
||||
* @property {Object} routes Object with all route configuration Objects as its properties.
|
||||
*
|
||||
* @description
|
||||
* `$route` is used for deep-linking URLs to controllers and views (HTML partials).
|
||||
@@ -262,102 +262,103 @@ function $RouteProvider(){
|
||||
* {@link ngRoute.$routeParams `$routeParams`} service.
|
||||
*
|
||||
* @example
|
||||
This example shows how changing the URL hash causes the `$route` to match a route against the
|
||||
URL, and the `ngView` pulls in the partial.
|
||||
|
||||
Note that this example is using {@link ng.directive:script inlined templates}
|
||||
to get it working on jsfiddle as well.
|
||||
|
||||
<example name="$route-service" module="ngRouteExample" deps="angular-route.js" fixBase="true">
|
||||
<file name="index.html">
|
||||
<div ng-controller="MainCntl">
|
||||
Choose:
|
||||
<a href="Book/Moby">Moby</a> |
|
||||
<a href="Book/Moby/ch/1">Moby: Ch1</a> |
|
||||
<a href="Book/Gatsby">Gatsby</a> |
|
||||
<a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
|
||||
<a href="Book/Scarlet">Scarlet Letter</a><br/>
|
||||
|
||||
<div ng-view></div>
|
||||
<hr />
|
||||
|
||||
<pre>$location.path() = {{$location.path()}}</pre>
|
||||
<pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
|
||||
<pre>$route.current.params = {{$route.current.params}}</pre>
|
||||
<pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
|
||||
<pre>$routeParams = {{$routeParams}}</pre>
|
||||
</div>
|
||||
</file>
|
||||
|
||||
<file name="book.html">
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{params.bookId}}<br />
|
||||
</file>
|
||||
|
||||
<file name="chapter.html">
|
||||
controller: {{name}}<br />
|
||||
Book Id: {{params.bookId}}<br />
|
||||
Chapter Id: {{params.chapterId}}
|
||||
</file>
|
||||
|
||||
<file name="script.js">
|
||||
angular.module('ngRouteExample', ['ngRoute'])
|
||||
|
||||
.config(function($routeProvider, $locationProvider) {
|
||||
$routeProvider.when('/Book/:bookId', {
|
||||
templateUrl: 'book.html',
|
||||
controller: BookCntl,
|
||||
resolve: {
|
||||
// I will cause a 1 second delay
|
||||
delay: function($q, $timeout) {
|
||||
var delay = $q.defer();
|
||||
$timeout(delay.resolve, 1000);
|
||||
return delay.promise;
|
||||
}
|
||||
}
|
||||
});
|
||||
$routeProvider.when('/Book/:bookId/ch/:chapterId', {
|
||||
templateUrl: 'chapter.html',
|
||||
controller: ChapterCntl
|
||||
});
|
||||
|
||||
// configure html5 to get links working on jsfiddle
|
||||
$locationProvider.html5Mode(true);
|
||||
});
|
||||
|
||||
function MainCntl($scope, $route, $routeParams, $location) {
|
||||
$scope.$route = $route;
|
||||
$scope.$location = $location;
|
||||
$scope.$routeParams = $routeParams;
|
||||
}
|
||||
|
||||
function BookCntl($scope, $routeParams) {
|
||||
$scope.name = "BookCntl";
|
||||
$scope.params = $routeParams;
|
||||
}
|
||||
|
||||
function ChapterCntl($scope, $routeParams) {
|
||||
$scope.name = "ChapterCntl";
|
||||
$scope.params = $routeParams;
|
||||
}
|
||||
</file>
|
||||
|
||||
<file name="protractor.js" type="protractor">
|
||||
it('should load and compile correct template', function() {
|
||||
element(by.linkText('Moby: Ch1')).click();
|
||||
var content = element(by.css('[ng-view]')).getText();
|
||||
expect(content).toMatch(/controller\: ChapterCntl/);
|
||||
expect(content).toMatch(/Book Id\: Moby/);
|
||||
expect(content).toMatch(/Chapter Id\: 1/);
|
||||
|
||||
element(by.partialLinkText('Scarlet')).click();
|
||||
|
||||
content = element(by.css('[ng-view]')).getText();
|
||||
expect(content).toMatch(/controller\: BookCntl/);
|
||||
expect(content).toMatch(/Book Id\: Scarlet/);
|
||||
});
|
||||
</file>
|
||||
</example>
|
||||
* This example shows how changing the URL hash causes the `$route` to match a route against the
|
||||
* URL, and the `ngView` pulls in the partial.
|
||||
*
|
||||
* Note that this example is using {@link ng.directive:script inlined templates}
|
||||
* to get it working on jsfiddle as well.
|
||||
*
|
||||
* <example name="$route-service" module="ngRouteExample"
|
||||
* deps="angular-route.js" fixBase="true">
|
||||
* <file name="index.html">
|
||||
* <div ng-controller="MainCntl">
|
||||
* Choose:
|
||||
* <a href="Book/Moby">Moby</a> |
|
||||
* <a href="Book/Moby/ch/1">Moby: Ch1</a> |
|
||||
* <a href="Book/Gatsby">Gatsby</a> |
|
||||
* <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
|
||||
* <a href="Book/Scarlet">Scarlet Letter</a><br/>
|
||||
*
|
||||
* <div ng-view></div>
|
||||
* <hr />
|
||||
*
|
||||
* <pre>$location.path() = {{$location.path()}}</pre>
|
||||
* <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
|
||||
* <pre>$route.current.params = {{$route.current.params}}</pre>
|
||||
* <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
|
||||
* <pre>$routeParams = {{$routeParams}}</pre>
|
||||
* </div>
|
||||
* </file>
|
||||
*
|
||||
* <file name="book.html">
|
||||
* controller: {{name}}<br />
|
||||
* Book Id: {{params.bookId}}<br />
|
||||
* </file>
|
||||
*
|
||||
* <file name="chapter.html">
|
||||
* controller: {{name}}<br />
|
||||
* Book Id: {{params.bookId}}<br />
|
||||
* Chapter Id: {{params.chapterId}}
|
||||
* </file>
|
||||
*
|
||||
* <file name="script.js">
|
||||
* angular.module('ngRouteExample', ['ngRoute'])
|
||||
*
|
||||
* .config(function($routeProvider, $locationProvider) {
|
||||
* $routeProvider.when('/Book/:bookId', {
|
||||
* templateUrl: 'book.html',
|
||||
* controller: BookCntl,
|
||||
* resolve: {
|
||||
* // I will cause a 1 second delay
|
||||
* delay: function($q, $timeout) {
|
||||
* var delay = $q.defer();
|
||||
* $timeout(delay.resolve, 1000);
|
||||
* return delay.promise;
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
* $routeProvider.when('/Book/:bookId/ch/:chapterId', {
|
||||
* templateUrl: 'chapter.html',
|
||||
* controller: ChapterCntl
|
||||
* });
|
||||
*
|
||||
* // configure html5 to get links working on jsfiddle
|
||||
* $locationProvider.html5Mode(true);
|
||||
* });
|
||||
*
|
||||
* function MainCntl($scope, $route, $routeParams, $location) {
|
||||
* $scope.$route = $route;
|
||||
* $scope.$location = $location;
|
||||
* $scope.$routeParams = $routeParams;
|
||||
* }
|
||||
*
|
||||
* function BookCntl($scope, $routeParams) {
|
||||
* $scope.name = "BookCntl";
|
||||
* $scope.params = $routeParams;
|
||||
* }
|
||||
*
|
||||
* function ChapterCntl($scope, $routeParams) {
|
||||
* $scope.name = "ChapterCntl";
|
||||
* $scope.params = $routeParams;
|
||||
* }
|
||||
* </file>
|
||||
*
|
||||
* <file name="protractor.js" type="protractor">
|
||||
* it('should load and compile correct template', function() {
|
||||
* element(by.linkText('Moby: Ch1')).click();
|
||||
* var content = element(by.css('[ng-view]')).getText();
|
||||
* expect(content).toMatch(/controller\: ChapterCntl/);
|
||||
* expect(content).toMatch(/Book Id\: Moby/);
|
||||
* expect(content).toMatch(/Chapter Id\: 1/);
|
||||
*
|
||||
* element(by.partialLinkText('Scarlet')).click();
|
||||
*
|
||||
* content = element(by.css('[ng-view]')).getText();
|
||||
* expect(content).toMatch(/controller\: BookCntl/);
|
||||
* expect(content).toMatch(/Book Id\: Scarlet/);
|
||||
* });
|
||||
* </file>
|
||||
* </example>
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -254,7 +254,7 @@ function htmlParser( html, handler ) {
|
||||
match = html.match( DOCTYPE_REGEXP );
|
||||
|
||||
if ( match ) {
|
||||
html = html.replace( match[0] , '');
|
||||
html = html.replace( match[0], '');
|
||||
chars = false;
|
||||
}
|
||||
// end tag
|
||||
|
||||
@@ -163,7 +163,7 @@ describe('injector', function() {
|
||||
function $f_n0 /*
|
||||
*/(
|
||||
$a, // x, <-- looks like an arg but it is a comment
|
||||
b_ , /* z, <-- looks like an arg but it is a
|
||||
b_, /* z, <-- looks like an arg but it is a
|
||||
multi-line comment
|
||||
function (a, b) {}
|
||||
*/
|
||||
|
||||
@@ -529,10 +529,18 @@ describe('$compile', function() {
|
||||
replace: true,
|
||||
template: '<th>TH</th>'
|
||||
}));
|
||||
directive('replaceWithThead', valueFn({
|
||||
replace: true,
|
||||
template: '<thead><tr><td>TD</td></tr></thead>'
|
||||
}));
|
||||
directive('replaceWithTbody', valueFn({
|
||||
replace: true,
|
||||
template: '<tbody><tr><td>TD</td></tr></tbody>'
|
||||
}));
|
||||
directive('replaceWithTfoot', valueFn({
|
||||
replace: true,
|
||||
template: '<tfoot><tr><td>TD</td></tr></tfoot>'
|
||||
}));
|
||||
}));
|
||||
|
||||
|
||||
@@ -718,12 +726,26 @@ describe('$compile', function() {
|
||||
expect(nodeName_(element)).toMatch(/th/i);
|
||||
}));
|
||||
|
||||
it('should support templates with root <thead> tags', inject(function($compile, $rootScope) {
|
||||
expect(function() {
|
||||
element = $compile('<div replace-with-thead></div>')($rootScope);
|
||||
}).not.toThrow();
|
||||
expect(nodeName_(element)).toMatch(/thead/i);
|
||||
}));
|
||||
|
||||
it('should support templates with root <tbody> tags', inject(function($compile, $rootScope) {
|
||||
expect(function() {
|
||||
element = $compile('<div replace-with-tbody></div>')($rootScope);
|
||||
}).not.toThrow();
|
||||
expect(nodeName_(element)).toMatch(/tbody/i);
|
||||
}));
|
||||
|
||||
it('should support templates with root <tfoot> tags', inject(function($compile, $rootScope) {
|
||||
expect(function() {
|
||||
element = $compile('<div replace-with-tfoot></div>')($rootScope);
|
||||
}).not.toThrow();
|
||||
expect(nodeName_(element)).toMatch(/tfoot/i);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@@ -833,10 +855,18 @@ describe('$compile', function() {
|
||||
replace: true,
|
||||
templateUrl: 'th.html'
|
||||
}));
|
||||
directive('replaceWithThead', valueFn({
|
||||
replace: true,
|
||||
templateUrl: 'thead.html'
|
||||
}));
|
||||
directive('replaceWithTbody', valueFn({
|
||||
replace: true,
|
||||
templateUrl: 'tbody.html'
|
||||
}));
|
||||
directive('replaceWithTfoot', valueFn({
|
||||
replace: true,
|
||||
templateUrl: 'tfoot.html'
|
||||
}));
|
||||
}
|
||||
));
|
||||
|
||||
@@ -1500,6 +1530,15 @@ describe('$compile', function() {
|
||||
expect(nodeName_(element)).toMatch(/th/i);
|
||||
}));
|
||||
|
||||
it('should support templates with root <thead> tags', inject(function($compile, $rootScope, $templateCache) {
|
||||
$templateCache.put('thead.html', '<thead><tr><td>TD</td></tr></thead>');
|
||||
expect(function() {
|
||||
element = $compile('<div replace-with-thead></div>')($rootScope);
|
||||
}).not.toThrow();
|
||||
$rootScope.$digest();
|
||||
expect(nodeName_(element)).toMatch(/thead/i);
|
||||
}));
|
||||
|
||||
it('should support templates with root <tbody> tags', inject(function($compile, $rootScope, $templateCache) {
|
||||
$templateCache.put('tbody.html', '<tbody><tr><td>TD</td></tr></tbody>');
|
||||
expect(function() {
|
||||
@@ -1508,6 +1547,15 @@ describe('$compile', function() {
|
||||
$rootScope.$digest();
|
||||
expect(nodeName_(element)).toMatch(/tbody/i);
|
||||
}));
|
||||
|
||||
it('should support templates with root <tfoot> tags', inject(function($compile, $rootScope, $templateCache) {
|
||||
$templateCache.put('tfoot.html', '<tfoot><tr><td>TD</td></tr></tfoot>');
|
||||
expect(function() {
|
||||
element = $compile('<div replace-with-tfoot></div>')($rootScope);
|
||||
}).not.toThrow();
|
||||
$rootScope.$digest();
|
||||
expect(nodeName_(element)).toMatch(/tfoot/i);
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -762,6 +762,718 @@ describe('input', function() {
|
||||
|
||||
|
||||
// INPUT TYPES
|
||||
describe('month', function (){
|
||||
it('should render blank if model is not a Date object', function() {
|
||||
compileInput('<input type="month" ng-model="january"/>');
|
||||
|
||||
scope.$apply(function(){
|
||||
scope.january = '2013-01';
|
||||
});
|
||||
|
||||
expect(inputElm.val()).toBe('');
|
||||
});
|
||||
|
||||
it('should set the view if the model is a valid Date object', function (){
|
||||
compileInput('<input type="month" ng-model="march"/>');
|
||||
|
||||
scope.$apply(function(){
|
||||
scope.march = new Date(2013, 2, 1);
|
||||
});
|
||||
|
||||
expect(inputElm.val()).toBe('2013-03');
|
||||
});
|
||||
|
||||
it('should set the model undefined if the input is an invalid month string', function () {
|
||||
compileInput('<input type="month" ng-model="value"/>');
|
||||
|
||||
scope.$apply(function(){
|
||||
scope.value = new Date(2013, 0, 1);
|
||||
});
|
||||
|
||||
|
||||
expect(inputElm.val()).toBe('2013-01');
|
||||
|
||||
try {
|
||||
//set to text for browsers with datetime-local validation.
|
||||
inputElm[0].setAttribute('type', 'text');
|
||||
} catch(e) {
|
||||
//for IE8
|
||||
}
|
||||
|
||||
changeInputValueTo('stuff');
|
||||
expect(inputElm.val()).toBe('stuff');
|
||||
expect(scope.value).toBeUndefined();
|
||||
expect(inputElm).toBeInvalid();
|
||||
});
|
||||
|
||||
it('should render as blank if null', function() {
|
||||
compileInput('<input type="month" ng-model="test" />');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = null;
|
||||
});
|
||||
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm.val()).toEqual('');
|
||||
});
|
||||
|
||||
it('should come up blank when no value specified', function() {
|
||||
compileInput('<input type="month" ng-model="test" />');
|
||||
|
||||
scope.$digest();
|
||||
expect(inputElm.val()).toBe('');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = null;
|
||||
});
|
||||
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm.val()).toBe('');
|
||||
});
|
||||
|
||||
|
||||
it('should parse empty string to null', function() {
|
||||
compileInput('<input type="month" ng-model="test" />');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = new Date(2011, 0, 1);
|
||||
});
|
||||
|
||||
changeInputValueTo('');
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm).toBeValid();
|
||||
});
|
||||
|
||||
|
||||
describe('min', function (){
|
||||
beforeEach(function (){
|
||||
compileInput('<input type="month" ng-model="value" name="alias" min="2013-01" />');
|
||||
scope.$digest();
|
||||
});
|
||||
|
||||
it('should invalidate', function (){
|
||||
changeInputValueTo('2012-12');
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeFalsy();
|
||||
expect(scope.form.alias.$error.min).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should validate', function (){
|
||||
changeInputValueTo('2013-07');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(+scope.value).toBe(+new Date(2013, 6, 1));
|
||||
expect(scope.form.alias.$error.min).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('max', function(){
|
||||
beforeEach(function (){
|
||||
compileInput('<input type="month" ng-model="value" name="alias" max="2013-01" />');
|
||||
scope.$digest();
|
||||
});
|
||||
|
||||
it('should validate', function (){
|
||||
changeInputValueTo('2012-03');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(+scope.value).toBe(+new Date(2012, 2, 1));
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should invalidate', function (){
|
||||
changeInputValueTo('2013-05');
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeUndefined();
|
||||
expect(scope.form.alias.$error.max).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('week', function (){
|
||||
it('should set render blank if model is not a Date object', function() {
|
||||
compileInput('<input type="week" ng-model="secondWeek"/>');
|
||||
|
||||
scope.$apply(function(){
|
||||
scope.secondWeek = '2013-W02';
|
||||
});
|
||||
|
||||
expect(inputElm.val()).toBe('');
|
||||
});
|
||||
|
||||
it('should set the view if the model is a valid Date object', function (){
|
||||
compileInput('<input type="week" ng-model="secondWeek"/>');
|
||||
|
||||
scope.$apply(function(){
|
||||
scope.secondWeek = new Date(2013, 0, 11);
|
||||
});
|
||||
|
||||
expect(inputElm.val()).toBe('2013-W02');
|
||||
});
|
||||
|
||||
it('should set the model undefined if the input is an invalid week string', function () {
|
||||
compileInput('<input type="week" ng-model="value"/>');
|
||||
|
||||
scope.$apply(function(){
|
||||
scope.value = new Date(2013, 0, 11);
|
||||
});
|
||||
|
||||
|
||||
expect(inputElm.val()).toBe('2013-W02');
|
||||
|
||||
try {
|
||||
//set to text for browsers with datetime-local validation.
|
||||
inputElm[0].setAttribute('type', 'text');
|
||||
} catch(e) {
|
||||
//for IE8
|
||||
}
|
||||
|
||||
changeInputValueTo('stuff');
|
||||
expect(inputElm.val()).toBe('stuff');
|
||||
expect(scope.value).toBeUndefined();
|
||||
expect(inputElm).toBeInvalid();
|
||||
});
|
||||
|
||||
it('should render as blank if null', function() {
|
||||
compileInput('<input type="week" ng-model="test" />');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = null;
|
||||
});
|
||||
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm.val()).toEqual('');
|
||||
});
|
||||
|
||||
it('should come up blank when no value specified', function() {
|
||||
compileInput('<input type="week" ng-model="test" />');
|
||||
|
||||
scope.$digest();
|
||||
expect(inputElm.val()).toBe('');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = null;
|
||||
});
|
||||
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm.val()).toBe('');
|
||||
});
|
||||
|
||||
|
||||
it('should parse empty string to null', function() {
|
||||
compileInput('<input type="week" ng-model="test" />');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = new Date(2011, 0, 1);
|
||||
});
|
||||
|
||||
changeInputValueTo('');
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm).toBeValid();
|
||||
});
|
||||
|
||||
describe('min', function (){
|
||||
beforeEach(function (){
|
||||
compileInput('<input type="week" ng-model="value" name="alias" min="2013-W01" />');
|
||||
scope.$digest();
|
||||
});
|
||||
|
||||
it('should invalidate', function (){
|
||||
changeInputValueTo('2012-W12');
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeFalsy();
|
||||
expect(scope.form.alias.$error.min).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should validate', function (){
|
||||
changeInputValueTo('2013-W03');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(+scope.value).toBe(+new Date(2013, 0, 17));
|
||||
expect(scope.form.alias.$error.min).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('max', function(){
|
||||
beforeEach(function (){
|
||||
compileInput('<input type="week" ng-model="value" name="alias" max="2013-W01" />');
|
||||
scope.$digest();
|
||||
});
|
||||
|
||||
it('should validate', function (){
|
||||
changeInputValueTo('2012-W01');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(+scope.value).toBe(+new Date(2012, 0, 5));
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should invalidate', function (){
|
||||
changeInputValueTo('2013-W03');
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeUndefined();
|
||||
expect(scope.form.alias.$error.max).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('datetime-local', function () {
|
||||
it('should render blank if model is not a Date object', function() {
|
||||
compileInput('<input type="datetime-local" ng-model="lunchtime"/>');
|
||||
|
||||
scope.$apply(function(){
|
||||
scope.lunchtime = '2013-12-16T11:30';
|
||||
});
|
||||
|
||||
expect(inputElm.val()).toBe('');
|
||||
});
|
||||
|
||||
it('should set the view if the model if a valid Date object.', function(){
|
||||
compileInput('<input type="datetime-local" ng-model="tenSecondsToNextYear"/>');
|
||||
|
||||
scope.$apply(function (){
|
||||
scope.tenSecondsToNextYear = new Date(2013, 11, 31, 23, 59);
|
||||
});
|
||||
|
||||
expect(inputElm.val()).toBe('2013-12-31T23:59');
|
||||
});
|
||||
|
||||
it('should set the model undefined if the view is invalid', function (){
|
||||
compileInput('<input type="datetime-local" ng-model="breakMe"/>');
|
||||
|
||||
scope.$apply(function (){
|
||||
scope.breakMe = new Date(2009, 0, 6, 16, 25);
|
||||
});
|
||||
|
||||
expect(inputElm.val()).toBe('2009-01-06T16:25');
|
||||
|
||||
try {
|
||||
//set to text for browsers with datetime-local validation.
|
||||
inputElm[0].setAttribute('type', 'text');
|
||||
} catch(e) {
|
||||
//for IE8
|
||||
}
|
||||
|
||||
changeInputValueTo('stuff');
|
||||
expect(inputElm.val()).toBe('stuff');
|
||||
expect(scope.breakMe).toBeUndefined();
|
||||
expect(inputElm).toBeInvalid();
|
||||
});
|
||||
|
||||
it('should render as blank if null', function() {
|
||||
compileInput('<input type="datetime-local" ng-model="test" />');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = null;
|
||||
});
|
||||
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm.val()).toEqual('');
|
||||
});
|
||||
|
||||
it('should come up blank when no value specified', function() {
|
||||
compileInput('<input type="datetime-local" ng-model="test" />');
|
||||
|
||||
scope.$digest();
|
||||
expect(inputElm.val()).toBe('');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = null;
|
||||
});
|
||||
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm.val()).toBe('');
|
||||
});
|
||||
|
||||
|
||||
it('should parse empty string to null', function() {
|
||||
compileInput('<input type="datetime-local" ng-model="test" />');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = new Date(2011, 0, 1);
|
||||
});
|
||||
|
||||
changeInputValueTo('');
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm).toBeValid();
|
||||
});
|
||||
|
||||
describe('min', function (){
|
||||
beforeEach(function (){
|
||||
compileInput('<input type="datetime-local" ng-model="value" name="alias" min="2000-01-01T12:30" />');
|
||||
scope.$digest();
|
||||
});
|
||||
|
||||
it('should invalidate', function (){
|
||||
changeInputValueTo('1999-12-31T01:02');
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeFalsy();
|
||||
expect(scope.form.alias.$error.min).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should validate', function (){
|
||||
changeInputValueTo('2000-01-01T23:02');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(+scope.value).toBe(+new Date(2000, 0, 1, 23, 2));
|
||||
expect(scope.form.alias.$error.min).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('max', function (){
|
||||
beforeEach(function (){
|
||||
compileInput('<input type="datetime-local" ng-model="value" name="alias" max="2019-01-01T01:02" />');
|
||||
scope.$digest();
|
||||
});
|
||||
|
||||
it('should invalidate', function (){
|
||||
changeInputValueTo('2019-12-31T01:02');
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeFalsy();
|
||||
expect(scope.form.alias.$error.max).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should validate', function() {
|
||||
changeInputValueTo('2000-01-01T01:02');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(+scope.value).toBe(+new Date(2000, 0, 1, 1, 2));
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate even if max value changes on-the-fly', function(done) {
|
||||
scope.max = '2013-01-01T01:02';
|
||||
compileInput('<input type="datetime-local" ng-model="value" name="alias" max="{{max}}" />');
|
||||
scope.$digest();
|
||||
|
||||
changeInputValueTo('2014-01-01T12:34');
|
||||
expect(inputElm).toBeInvalid();
|
||||
|
||||
scope.max = '2001-01-01T01:02';
|
||||
scope.$digest(function () {
|
||||
expect(inputElm).toBeValid();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate even if min value changes on-the-fly', function(done) {
|
||||
scope.min = '2013-01-01T01:02';
|
||||
compileInput('<input type="datetime-local" ng-model="value" name="alias" min="{{min}}" />');
|
||||
scope.$digest();
|
||||
|
||||
changeInputValueTo('2010-01-01T12:34');
|
||||
expect(inputElm).toBeInvalid();
|
||||
|
||||
scope.min = '2014-01-01T01:02';
|
||||
scope.$digest(function () {
|
||||
expect(inputElm).toBeValid();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('time', function () {
|
||||
it('should render blank if model is not a Date object', function() {
|
||||
compileInput('<input type="time" ng-model="lunchtime"/>');
|
||||
|
||||
scope.$apply(function(){
|
||||
scope.lunchtime = '11:30';
|
||||
});
|
||||
|
||||
expect(inputElm.val()).toBe('');
|
||||
});
|
||||
|
||||
it('should set the view if the model if a valid Date object.', function(){
|
||||
compileInput('<input type="time" ng-model="threeFortyOnePm"/>');
|
||||
|
||||
scope.$apply(function (){
|
||||
scope.threeFortyOnePm = new Date(0, 0, 1, 15, 41);
|
||||
});
|
||||
|
||||
expect(inputElm.val()).toBe('15:41');
|
||||
});
|
||||
|
||||
it('should set the model undefined if the view is invalid', function (){
|
||||
compileInput('<input type="time" ng-model="breakMe"/>');
|
||||
|
||||
scope.$apply(function (){
|
||||
scope.breakMe = new Date(0, 0, 1, 16, 25);
|
||||
});
|
||||
|
||||
expect(inputElm.val()).toBe('16:25');
|
||||
|
||||
try {
|
||||
//set to text for browsers with time validation.
|
||||
inputElm[0].setAttribute('type', 'text');
|
||||
} catch(e) {
|
||||
//for IE8
|
||||
}
|
||||
|
||||
changeInputValueTo('stuff');
|
||||
expect(inputElm.val()).toBe('stuff');
|
||||
expect(scope.breakMe).toBeUndefined();
|
||||
expect(inputElm).toBeInvalid();
|
||||
});
|
||||
|
||||
it('should render as blank if null', function() {
|
||||
compileInput('<input type="time" ng-model="test" />');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = null;
|
||||
});
|
||||
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm.val()).toEqual('');
|
||||
});
|
||||
|
||||
it('should come up blank when no value specified', function() {
|
||||
compileInput('<input type="time" ng-model="test" />');
|
||||
|
||||
scope.$digest();
|
||||
expect(inputElm.val()).toBe('');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = null;
|
||||
});
|
||||
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm.val()).toBe('');
|
||||
});
|
||||
|
||||
|
||||
it('should parse empty string to null', function() {
|
||||
compileInput('<input type="time" ng-model="test" />');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = new Date(2011, 0, 1);
|
||||
});
|
||||
|
||||
changeInputValueTo('');
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm).toBeValid();
|
||||
});
|
||||
|
||||
describe('min', function (){
|
||||
beforeEach(function (){
|
||||
compileInput('<input type="time" ng-model="value" name="alias" min="09:30" />');
|
||||
scope.$digest();
|
||||
});
|
||||
|
||||
it('should invalidate', function (){
|
||||
changeInputValueTo('01:02');
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeFalsy();
|
||||
expect(scope.form.alias.$error.min).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should validate', function (){
|
||||
changeInputValueTo('23:02');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(+scope.value).toBe(+new Date(0, 0, 1, 23, 2));
|
||||
expect(scope.form.alias.$error.min).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('max', function (){
|
||||
beforeEach(function (){
|
||||
compileInput('<input type="time" ng-model="value" name="alias" max="22:30" />');
|
||||
scope.$digest();
|
||||
});
|
||||
|
||||
it('should invalidate', function (){
|
||||
changeInputValueTo('23:00');
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeFalsy();
|
||||
expect(scope.form.alias.$error.max).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should validate', function() {
|
||||
changeInputValueTo('05:30');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(+scope.value).toBe(+new Date(0, 0, 1, 5, 30));
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate even if max value changes on-the-fly', function(done) {
|
||||
scope.max = '21:02';
|
||||
compileInput('<input type="time" ng-model="value" name="alias" max="{{max}}" />');
|
||||
scope.$digest();
|
||||
|
||||
changeInputValueTo('22:34');
|
||||
expect(inputElm).toBeInvalid();
|
||||
|
||||
scope.max = '12:34';
|
||||
scope.$digest(function () {
|
||||
expect(inputElm).toBeValid();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate even if min value changes on-the-fly', function(done) {
|
||||
scope.min = '08:45';
|
||||
compileInput('<input type="time" ng-model="value" name="alias" min="{{min}}" />');
|
||||
scope.$digest();
|
||||
|
||||
changeInputValueTo('06:15');
|
||||
expect(inputElm).toBeInvalid();
|
||||
|
||||
scope.min = '13:50';
|
||||
scope.$digest(function () {
|
||||
expect(inputElm).toBeValid();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('date', function () {
|
||||
it('should render blank if model is not a Date object.', function() {
|
||||
compileInput('<input type="date" ng-model="birthday"/>');
|
||||
|
||||
scope.$apply(function(){
|
||||
scope.birthday = '1977-10-22';
|
||||
});
|
||||
|
||||
expect(inputElm.val()).toBe('');
|
||||
});
|
||||
|
||||
it('should set the view if the model if a valid Date object.', function(){
|
||||
compileInput('<input type="date" ng-model="christmas"/>');
|
||||
|
||||
scope.$apply(function (){
|
||||
scope.christmas = new Date(2013, 11, 25);
|
||||
});
|
||||
|
||||
expect(inputElm.val()).toBe('2013-12-25');
|
||||
});
|
||||
|
||||
it('should set the model undefined if the view is invalid', function (){
|
||||
compileInput('<input type="date" ng-model="arrMatey"/>');
|
||||
|
||||
scope.$apply(function (){
|
||||
scope.arrMatey = new Date(2014, 8, 14);
|
||||
});
|
||||
|
||||
expect(inputElm.val()).toBe('2014-09-14');
|
||||
|
||||
try {
|
||||
//set to text for browsers with date validation.
|
||||
inputElm[0].setAttribute('type', 'text');
|
||||
} catch(e) {
|
||||
//for IE8
|
||||
}
|
||||
|
||||
changeInputValueTo('1-2-3');
|
||||
expect(inputElm.val()).toBe('1-2-3');
|
||||
expect(scope.arrMatey).toBeUndefined();
|
||||
expect(inputElm).toBeInvalid();
|
||||
});
|
||||
|
||||
it('should render as blank if null', function() {
|
||||
compileInput('<input type="date" ng-model="test" />');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = null;
|
||||
});
|
||||
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm.val()).toEqual('');
|
||||
});
|
||||
|
||||
it('should come up blank when no value specified', function() {
|
||||
compileInput('<input type="date" ng-model="test" />');
|
||||
|
||||
scope.$digest();
|
||||
expect(inputElm.val()).toBe('');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = null;
|
||||
});
|
||||
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm.val()).toBe('');
|
||||
});
|
||||
|
||||
|
||||
it('should parse empty string to null', function() {
|
||||
compileInput('<input type="date" ng-model="test" />');
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.test = new Date(2011, 0, 1);
|
||||
});
|
||||
|
||||
changeInputValueTo('');
|
||||
expect(scope.test).toBeNull();
|
||||
expect(inputElm).toBeValid();
|
||||
});
|
||||
|
||||
describe('min', function (){
|
||||
beforeEach(function (){
|
||||
compileInput('<input type="date" ng-model="value" name="alias" min="2000-01-01" />');
|
||||
scope.$digest();
|
||||
});
|
||||
|
||||
it('should invalidate', function (){
|
||||
changeInputValueTo('1999-12-31');
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeFalsy();
|
||||
expect(scope.form.alias.$error.min).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should validate', function (){
|
||||
changeInputValueTo('2000-01-01');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(+scope.value).toBe(+new Date(2000, 0, 1));
|
||||
expect(scope.form.alias.$error.min).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('max', function (){
|
||||
beforeEach(function (){
|
||||
compileInput('<input type="date" ng-model="value" name="alias" max="2019-01-01" />');
|
||||
scope.$digest();
|
||||
});
|
||||
|
||||
it('should invalidate', function (){
|
||||
changeInputValueTo('2019-12-31');
|
||||
expect(inputElm).toBeInvalid();
|
||||
expect(scope.value).toBeFalsy();
|
||||
expect(scope.form.alias.$error.max).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should validate', function() {
|
||||
changeInputValueTo('2000-01-01');
|
||||
expect(inputElm).toBeValid();
|
||||
expect(+scope.value).toBe(+new Date(2000, 0, 1));
|
||||
expect(scope.form.alias.$error.max).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate even if max value changes on-the-fly', function(done) {
|
||||
scope.max = '2013-01-01';
|
||||
compileInput('<input type="date" ng-model="value" name="alias" max="{{max}}" />');
|
||||
scope.$digest();
|
||||
|
||||
changeInputValueTo('2014-01-01');
|
||||
expect(inputElm).toBeInvalid();
|
||||
|
||||
scope.max = '2001-01-01';
|
||||
scope.$digest(function () {
|
||||
expect(inputElm).toBeValid();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate even if min value changes on-the-fly', function(done) {
|
||||
scope.min = '2013-01-01';
|
||||
compileInput('<input type="date" ng-model="value" name="alias" min="{{min}}" />');
|
||||
scope.$digest();
|
||||
|
||||
changeInputValueTo('2010-01-01');
|
||||
expect(inputElm).toBeInvalid();
|
||||
|
||||
scope.min = '2014-01-01';
|
||||
scope.$digest(function () {
|
||||
expect(inputElm).toBeValid();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('number', function() {
|
||||
|
||||
@@ -798,7 +1510,6 @@ describe('input', function() {
|
||||
expect(inputElm.val()).toEqual('');
|
||||
});
|
||||
|
||||
|
||||
it('should come up blank when no value specified', function() {
|
||||
compileInput('<input type="number" ng-model="age" />');
|
||||
|
||||
|
||||
@@ -9,13 +9,65 @@ describe('style', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should not compile style element', inject(function($compile, $rootScope) {
|
||||
element = jqLite('<style type="text/css">should {{notBound}}</style>');
|
||||
it('should compile style element without binding', inject(function($compile, $rootScope) {
|
||||
element = jqLite('<style type="text/css">.header{font-size:1.5em; h3{font-size:1.5em}}</style>');
|
||||
$compile(element)($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
// read innerHTML and trim to pass on IE8
|
||||
expect(trim(element[0].innerHTML)).toBe('should {{notBound}}');
|
||||
expect(trim(element[0].innerHTML)).toBe('.header{font-size:1.5em; h3{font-size:1.5em}}');
|
||||
}));
|
||||
|
||||
|
||||
it('should compile style element with one simple bind', inject(function($compile, $rootScope) {
|
||||
element = jqLite('<style type="text/css">.some-container{ width: {{elementWidth}}px; }</style>');
|
||||
$compile(element)($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
// read innerHTML and trim to pass on IE8
|
||||
expect(trim(element[0].innerHTML)).toBe('.some-container{ width: px; }');
|
||||
|
||||
$rootScope.$apply(function() {
|
||||
$rootScope.elementWidth = 200;
|
||||
});
|
||||
|
||||
// read innerHTML and trim to pass on IE8
|
||||
expect(trim(element[0].innerHTML)).toBe('.some-container{ width: 200px; }');
|
||||
}));
|
||||
|
||||
|
||||
it('should compile style element with one bind', inject(function($compile, $rootScope) {
|
||||
element = jqLite('<style type="text/css">.header{ h3 { font-size: {{fontSize}}em }}</style>');
|
||||
$compile(element)($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
// read innerHTML and trim to pass on IE8
|
||||
expect(trim(element[0].innerHTML)).toBe('.header{ h3 { font-size: em }}');
|
||||
|
||||
$rootScope.$apply(function() {
|
||||
$rootScope.fontSize = 1.5;
|
||||
});
|
||||
|
||||
// read innerHTML and trim to pass on IE8
|
||||
expect(trim(element[0].innerHTML)).toBe('.header{ h3 { font-size: 1.5em }}');
|
||||
}));
|
||||
|
||||
|
||||
it('should compile style element with two binds', inject(function($compile, $rootScope) {
|
||||
element = jqLite('<style type="text/css">.header{ h3 { font-size: {{fontSize}}{{unit}} }}</style>');
|
||||
$compile(element)($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
// read innerHTML and trim to pass on IE8
|
||||
expect(trim(element[0].innerHTML)).toBe('.header{ h3 { font-size: }}');
|
||||
|
||||
$rootScope.$apply(function() {
|
||||
$rootScope.fontSize = 1.5;
|
||||
$rootScope.unit = 'em';
|
||||
});
|
||||
|
||||
// read innerHTML and trim to pass on IE8
|
||||
expect(trim(element[0].innerHTML)).toBe('.header{ h3 { font-size: 1.5em }}');
|
||||
}));
|
||||
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ describe('filters', function() {
|
||||
var noon = new angular.mock.TzDate(+5, '2010-09-03T17:05:08.012Z'); //12pm
|
||||
var midnight = new angular.mock.TzDate(+5, '2010-09-03T05:05:08.123Z'); //12am
|
||||
var earlyDate = new angular.mock.TzDate(+5, '0001-09-03T05:05:08.000Z');
|
||||
|
||||
var secondWeek = new angular.mock.TzDate(+5, '2013-01-11T12:00:00.000Z'); //Friday Jan 11, 2012
|
||||
var date;
|
||||
|
||||
beforeEach(inject(function($filter) {
|
||||
@@ -220,6 +220,12 @@ describe('filters', function() {
|
||||
});
|
||||
|
||||
it('should accept various format strings', function() {
|
||||
expect(date(secondWeek, 'yyyy-Ww')).
|
||||
toEqual('2013-W2');
|
||||
|
||||
expect(date(secondWeek, 'yyyy-Www')).
|
||||
toEqual('2013-W02');
|
||||
|
||||
expect(date(morning, "yy-MM-dd HH:mm:ss")).
|
||||
toEqual('10-09-03 07:05:08');
|
||||
|
||||
|
||||
@@ -456,7 +456,17 @@ describe('$httpBackend', function() {
|
||||
}
|
||||
|
||||
|
||||
it('should convert 0 to 200 if content', function() {
|
||||
it('should convert 0 to 200 if content and file protocol', function() {
|
||||
$backend = createHttpBackend($browser, createMockXhr);
|
||||
|
||||
$backend('GET', 'file:///whatever/index.html', null, callback);
|
||||
respond(0, 'SOME CONTENT');
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(callback.mostRecentCall.args[0]).toBe(200);
|
||||
});
|
||||
|
||||
it('should convert 0 to 200 if content for protocols other than file', function() {
|
||||
$backend = createHttpBackend($browser, createMockXhr);
|
||||
|
||||
$backend('GET', 'someProtocol:///whatever/index.html', null, callback);
|
||||
@@ -466,17 +476,25 @@ describe('$httpBackend', function() {
|
||||
expect(callback.mostRecentCall.args[0]).toBe(200);
|
||||
});
|
||||
|
||||
|
||||
it('should convert 0 to 404 if no content', function() {
|
||||
it('should convert 0 to 404 if no content and file protocol', function() {
|
||||
$backend = createHttpBackend($browser, createMockXhr);
|
||||
|
||||
$backend('GET', 'someProtocol:///whatever/index.html', null, callback);
|
||||
$backend('GET', 'file:///whatever/index.html', null, callback);
|
||||
respond(0, '');
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(callback.mostRecentCall.args[0]).toBe(404);
|
||||
});
|
||||
|
||||
it('should not convert 0 to 404 if no content for protocols other than file', function() {
|
||||
$backend = createHttpBackend($browser, createMockXhr);
|
||||
|
||||
$backend('GET', 'someProtocol:///whatever/index.html', null, callback);
|
||||
respond(0, '');
|
||||
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(callback.mostRecentCall.args[0]).toBe(0);
|
||||
});
|
||||
|
||||
it('should convert 0 to 404 if no content - relative url', function() {
|
||||
var originalUrlParsingNode = urlParsingNode;
|
||||
@@ -486,10 +504,10 @@ describe('$httpBackend', function() {
|
||||
hash : "#/C:/",
|
||||
host : "",
|
||||
hostname : "",
|
||||
href : "someProtocol:///C:/base#!/C:/foo",
|
||||
href : "file:///C:/base#!/C:/foo",
|
||||
pathname : "/C:/foo",
|
||||
port : "",
|
||||
protocol : "someProtocol:",
|
||||
protocol : "file:",
|
||||
search : "",
|
||||
setAttribute: angular.noop
|
||||
};
|
||||
|
||||
@@ -31,6 +31,38 @@ describe('$$rAF', function() {
|
||||
expect(present).toBe(true);
|
||||
}));
|
||||
|
||||
describe('$timeout fallback', function() {
|
||||
it("it should use a $timeout incase native rAF isn't suppored", function() {
|
||||
var timeoutSpy = jasmine.createSpy('callback');
|
||||
|
||||
//we need to create our own injector to work around the ngMock overrides
|
||||
var injector = createInjector(['ng', function($provide) {
|
||||
$provide.value('$timeout', timeoutSpy);
|
||||
$provide.decorator('$window', function($delegate) {
|
||||
$delegate.requestAnimationFrame = false;
|
||||
$delegate.webkitRequestAnimationFrame = false;
|
||||
$delegate.mozRequestAnimationFrame = false;
|
||||
return $delegate;
|
||||
});
|
||||
}]);
|
||||
|
||||
var $$rAF = injector.get('$$rAF');
|
||||
expect($$rAF.supported).toBe(false);
|
||||
|
||||
var message;
|
||||
$$rAF(function() {
|
||||
message = 'on';
|
||||
});
|
||||
|
||||
expect(message).toBeUndefined();
|
||||
expect(timeoutSpy).toHaveBeenCalled();
|
||||
|
||||
timeoutSpy.mostRecentCall.args[0]();
|
||||
|
||||
expect(message).toBe('on');
|
||||
});
|
||||
});
|
||||
|
||||
describe('mocks', function() {
|
||||
it('should throw an error if no frames are present', inject(function($$rAF) {
|
||||
if($$rAF.supported) {
|
||||
|
||||
@@ -37,13 +37,6 @@ describe('sanitizeUri', function() {
|
||||
expect(sanitizeImg(testUrl)).toBe("unsafe:data:,foo");
|
||||
});
|
||||
|
||||
it('should not sanitize data: URIs for images', function() {
|
||||
// image data uri
|
||||
// ref: http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever
|
||||
testUrl = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
|
||||
expect(sanitizeImg(testUrl)).toBe('data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');
|
||||
});
|
||||
|
||||
it('should sanitize mailto: urls', function() {
|
||||
testUrl = "mailto:foo@bar.com";
|
||||
expect(sanitizeImg(testUrl)).toBe('unsafe:mailto:foo@bar.com');
|
||||
@@ -113,6 +106,17 @@ describe('sanitizeUri', function() {
|
||||
expect(sanitizeImg(testUrl)).toBe('file:///foo/bar.html');
|
||||
});
|
||||
|
||||
it('should not sanitize blob urls', function() {
|
||||
testUrl = "blob:///foo/bar.html";
|
||||
expect(sanitizeImg(testUrl)).toBe('blob:///foo/bar.html');
|
||||
});
|
||||
|
||||
it('should not sanitize data: URIs for images', function() {
|
||||
// image data uri
|
||||
// ref: http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever
|
||||
testUrl = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
|
||||
expect(sanitizeImg(testUrl)).toBe('data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==');
|
||||
});
|
||||
|
||||
it('should allow reconfiguration of the src whitelist', function() {
|
||||
var returnVal;
|
||||
@@ -227,4 +231,4 @@ describe('sanitizeUri', function() {
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
+1
-1
@@ -109,7 +109,7 @@ describe('SCE', function() {
|
||||
}));
|
||||
|
||||
it('should NOT wrap unknown contexts', inject(function($sce) {
|
||||
expect(function() { $sce.trustAs('unknown1' , '123'); }).toThrowMinErr(
|
||||
expect(function() { $sce.trustAs('unknown1', '123'); }).toThrowMinErr(
|
||||
'$sce', 'icontext', 'Attempted to trust a value in invalid context. Context: unknown1; Value: 123');
|
||||
}));
|
||||
|
||||
|
||||
@@ -3356,6 +3356,49 @@ describe("ngAnimate", function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should animate only the specified CSS className inside ng-if', function() {
|
||||
var captures = {};
|
||||
module(function($animateProvider) {
|
||||
$animateProvider.classNameFilter(/prefixed-animation/);
|
||||
$animateProvider.register('.capture', function() {
|
||||
return {
|
||||
enter : buildFn('enter'),
|
||||
leave : buildFn('leave')
|
||||
};
|
||||
|
||||
function buildFn(key) {
|
||||
return function(element, className, done) {
|
||||
captures[key] = true;
|
||||
(done || className)();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
inject(function($rootScope, $compile, $rootElement, $document, $sniffer, $animate) {
|
||||
if(!$sniffer.transitions) return;
|
||||
|
||||
var upperElement = $compile('<div><div ng-if=1><span class="capture prefixed-animation"></span></div></div>')($rootScope);
|
||||
$rootElement.append(upperElement);
|
||||
jqLite($document[0].body).append($rootElement);
|
||||
|
||||
$rootScope.$digest();
|
||||
$animate.triggerCallbacks();
|
||||
|
||||
var element = upperElement.find('span');
|
||||
|
||||
var leaveDone = false;
|
||||
$animate.leave(element, function() {
|
||||
leaveDone = true;
|
||||
});
|
||||
|
||||
$rootScope.$digest();
|
||||
$animate.triggerCallbacks();
|
||||
|
||||
expect(captures['leave']).toBe(true);
|
||||
expect(leaveDone).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should respect the most relevant CSS transition property if defined in multiple classes',
|
||||
inject(function($sniffer, $compile, $rootScope, $rootElement, $animate, $timeout) {
|
||||
|
||||
|
||||
@@ -222,6 +222,6 @@ var swipeTests = function(description, restrictBrowsers, startEvent, moveEvent,
|
||||
});
|
||||
}
|
||||
|
||||
swipeTests('touch', true /* restrictBrowers */, 'touchstart', 'touchmove', 'touchend');
|
||||
swipeTests('mouse', false /* restrictBrowers */, 'mousedown', 'mousemove', 'mouseup');
|
||||
swipeTests('touch', /* restrictBrowers */ true, 'touchstart', 'touchmove', 'touchend');
|
||||
swipeTests('mouse', /* restrictBrowers */ false, 'mousedown', 'mousemove', 'mouseup');
|
||||
|
||||
|
||||
@@ -387,6 +387,6 @@ var swipeTests = function(description, restrictBrowsers, startEvent, moveEvent,
|
||||
});
|
||||
}
|
||||
|
||||
swipeTests('touch', true /* restrictBrowers */, 'touchstart', 'touchmove', 'touchend');
|
||||
swipeTests('mouse', false /* restrictBrowers */, 'mousedown', 'mousemove', 'mouseup');
|
||||
swipeTests('touch', /* restrictBrowers */ true, 'touchstart', 'touchmove', 'touchend');
|
||||
swipeTests('mouse', /* restrictBrowers */ false, 'mousedown', 'mousemove', 'mouseup');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user