Compare commits

...

72 Commits

Author SHA1 Message Date
Vojta Jina 9bfbb16e23 chore(scripts): fix the versions script again 2014-03-14 16:26:40 -07:00
Vojta Jina 2b741dc8b8 chore(scripts): fix the versions script 2014-03-14 15:59:23 -07:00
Vojta Jina e888dde3c5 chore(scripts): disable testing seed and phonecat during a release
This reverts commit d5294ebfa0.

It turned out to be more work and I don't wanna deal with it right now.
2014-03-14 14:48:56 -07:00
Vojta Jina d5294ebfa0 chore(scripts): test seed and phonecat during a release 2014-03-14 14:24:43 -07:00
Vojta Jina 6d6ebf7c61 chore(scripts): make the release script more flexible
Now the SHA can be short/long, whateva.
2014-03-14 14:24:21 -07:00
Jeff Cross 44b940e88d chore: update changelog for 1.3.0-beta.2 2014-03-14 14:02:35 -07:00
Pawel Kozlowski 56e73ea355 fix($http): don't covert 0 status codes to 404 for non-file protocols
PR #5547 introduced conversion of all 0 status codes to 404 for cases
where no response was recieved (previously this was done for the
file:// protocol only). But this mechanism is too eager and
masks legitimate cases where status 0 should be returned. This commits
reverts to the previous mechanism of handling 0 status code for the
file:// protocol (converting 0 to 404) while retaining the returned
status code 0 for all the protocols other than file://

Fixes #6074
Fixes #6155
2014-03-14 13:44:56 -07:00
Vojta Jina bfb6af7053 chore: make compare-master-to-stable script more flexible 2014-03-14 11:43:15 -07:00
Vojta Jina d7be9588a0 chore: update compare-master-to-stable to use v1.2.x 2014-03-14 11:26:30 -07:00
Vojta Jina 53e4da8eab chore: fix compare-master-to-stable script 2014-03-14 11:24:24 -07:00
Matias Niemelä 7b5e019981 fix($$rAF): always fallback to a $timeout incase native rAF isn't supported
Closes #6654
2014-03-14 12:42:07 -04:00
Tomer Chachamu 129e2e021a fix(ngAnimate): setting classNameFilter disables animation inside ng-if
Closes #6539
2014-03-14 12:40:40 -04:00
Peter Bacon Darwin 3cc02e7f03 docs(scripts/utils.inc): clarify documentation 2014-03-14 11:09:36 +00:00
Nick Heiner 79592ce9e2 docs(ngMock): grammar nitpick. 2014-03-13 16:52:59 -07:00
Sagie Maoz a9a38d84b9 docs(guide/compiler): add missing closing parenthesis 2014-03-13 16:39:23 -07:00
Nick Carter 24a67f9515 docs(guide/unit-testing): fix typo 2014-03-13 16:37:33 -07:00
Wesley Cho 91ef3a31a0 docs($resource): fix example using promise 2014-03-13 16:33:38 -07:00
Thomas Belin cea44b3e86 fix (ngAnimate): fix requestAnimationFrame for old version of Firefox
The recent $$RAFProvider which is a wrapper for the native
requestAnimationFrame method doesn't use the mozRequestAnimationFrame.
Old versions of FF (20 for example) crash if ngAnimate is included

No breaking changes and fix issue https://github.com/angular/angular.js/issues/6535

Closes #6535
Closes #6540
2014-03-13 16:31:11 -07:00
Tobias Bosch e8c6b9bf25 chore(CHANGELOG.md): add input type date PR as breaking change
Related to #6630
2014-03-13 16:03:20 -07:00
Peter Bacon Darwin 5412372e93 chore(shrinkwrap): update dgeni-packages 2014-03-13 18:33:10 +00:00
Peter Bacon Darwin 4f823f902d chore(shrinkwrap): re-run shrinkwrap locally
This will make the following commit clearer when the update is run.
2014-03-13 18:33:10 +00:00
Tobias Bosch fe0e434a87 chore(version-info): use remote tags and increment patch version 2014-03-12 15:19:48 -07:00
Peter Bacon Darwin edad4e63df chore(version-info): better error msg if not tags 2014-03-12 19:22:32 +00:00
Igor Minar f684cb09a5 chore(npm): add shrinkwrap to lock down dependencies
We need to be able to build angular at older shas, without the lock file / shrinkwrap file
the dependencies will resolve differently on different machines and at different times.

This will help us avoid broken builds and hard to track down issues.

I had to manually edit this file after it was generated because `npm shrinkwrap` will install
optional dependencies as if they were hard dependencies.

See: https://github.com/npm/npm/issues/2679#issuecomment-37361236

My manual edit:

```
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index 756df44..dc157eb 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -3110,19 +3110,7 @@
         "chokidar": {
           "version": "0.8.1",
           "from": "https://registry.npmjs.org/chokidar/-/chokidar-0.8.1.tgz",
-          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-0.8.1.tgz",
-          "dependencies": {
-            "fsevents": {
-              "version": "0.1.6",
-              "from": "fsevents@0.1.6",
-              "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-0.1.6.tgz"
-            },
-            "recursive-readdir": {
-              "version": "0.0.2",
-              "from": "recursive-readdir@0.0.2",
-              "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-0.0.2.tgz"
-            }
-          }
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-0.8.1.tgz"
         },
         "glob": {
           "version": "3.2.9",
```

Additionally chokidar doesn't list the dependencies above as optional, but that will hopefully
be soon fixed: https://github.com/paulmillr/chokidar/pull/106

In the meantime the patch from the PR above needs to be applied to
node_modules/karma/node_modules/chokidar/package.json before running `npm shrinkwrap`

----

After this change is applied, angular core developers don't need to do anything differently,
except when updating dependencies we need to call `npm update && npm shrinkwrap --dev`
followed by reappling my patch above until npm's bug.

Closes #6653
2014-03-11 22:44:37 -07:00
Peter Bacon Darwin d7717d93e4 chore(doc-gen): fix dependencyPath 2014-03-11 20:24:34 +00:00
Peter Bacon Darwin 247ec19c82 chore(package.json): update dgeni-packages dependency
The new version of dgeni-packages/ngdoc generates a manifest for each
example that can be used by plunker.
2014-03-11 19:10:34 +00:00
Stéphane Reynaud 78165c224d docs(tutorial): display button icons (Previous, Live Demo, ...)
In relation to https://github.com/angular/dgeni-packages/pull/8

Closes #6641
2014-03-11 10:19:42 +00:00
Peter Bacon Darwin d1214af132 chore(build): refactor build version information 2014-03-11 06:35:19 +00:00
Peter Bacon Darwin 11c5bb7f3d docs(versions): rework the version info extraction
The docs were relying on the grunt/util module for getting version info
but this was unreliable and full of custom regexes.  This is moved into
a new version-info module that makes much better use of the semver library.
2014-03-11 06:35:19 +00:00
Louis Haußknecht d6419d0aff docs(route.js): changed html entities lt gt to < and > 2014-03-10 15:37:44 -07:00
Basem Mostafa 55848a9139 docs(ngRepeat): Separate animation class in new lines
Moving to new lines & making it bold to avoid confusion
when they r all in same line without any separation

Closes #6633
2014-03-10 15:34:16 -07:00
Matias Niemelä 0f13f24ad2 chore(docs): ensure the "Improve this doc" button is clickable
Closes #6631
2014-03-10 15:09:25 -04:00
doodeec 0d8de2d3ea docs($route): change routes property to correct type
change $route.routes property type to Object, property is marked incorrectly as an Array

Closes #6552
2014-03-10 02:19:57 -07:00
Narretz 7833ce0a6e docs(guide/forms): remove unnecessary controller reference
the controller reference was breaking the custom validation example

Closes #6525
Closes #6533
2014-03-10 02:14:50 -07:00
Igor Minar 47ab8df455 feat(): whitelist blob urls for sanitization of data-bound image urls
Closes #4623
2014-03-10 01:39:15 -07:00
chadfennell b700282ffd docs(guide/providers): remove unneeded word "the"
no need to specify which space, there's only one :)

Closes #6622
2014-03-09 13:25:29 -04:00
Chung-Min Cheng 1b9395ea8f docs(tutorial/step-12): correct application name
Fixed wrong app name:
- phonecat -> phonecatApp, which meets the code in app.js

Closes #6611
2014-03-08 18:32:32 +01:00
Peter Bacon Darwin 44d160e3ce docs(Error404): better heading 2014-03-08 07:06:20 +00:00
Peter Bacon Darwin 4f90c9b531 docs(Error404): improve search results layout 2014-03-08 06:34:47 +00:00
jenkins 11aceac273 chore(release): update cdn version 2014-03-07 17:03:12 -08:00
Tobias Bosch f08bf6f1f7 chore(release): Update package.json to reflect 1.3.x version 2014-03-07 16:23:14 -08:00
Brian Ford ca4ddfadba docs(changelog): release notes for 1.3.0-beta.1 retractable-eyebrow 2014-03-07 15:27:39 -08:00
Sekib Omazic 4bab3d8227 docs($sce): correct typo
`consititute` -> `constitute`

Typo fixed

Closes #6607
2014-03-07 18:12:40 -05:00
Igor Minar b12c6b485d chore(build): upgrade grunt-jscs-checker to ~0.4.0
this is primarily to resolve peerdependency version mismatch issue
2014-03-07 15:06:53 -08:00
Sekib Omazic 9c353b4f17 docs(ngBind): fix typo
`preferrable` -> `preferable`

Typo fixed

Closes #6606
2014-03-07 17:55:10 -05:00
Igor Minar 21243d62a2 docs(guide/migration): fix broken link 2014-03-07 13:51:42 -08:00
Sekib Omazic ad309b1332 docs(guide/migration): fix typos
A few typos fixed.

Closes #6605
2014-03-07 16:30:40 -05:00
Sekib Omazic 7a75356388 docs(guide/directive): fix typo
`restictions` -> `restrictions`

Closes #6604
2014-03-07 16:15:54 -05:00
Peter Bacon Darwin dc57fe97e1 style(jsdoc tags): remove/ammend invalid tags
As highlighted by the new sterner dgeni.
2014-03-07 20:05:59 +00:00
Peter Bacon Darwin 853999de10 docs(404 errors): provide a better 404 experience
It is a bit rough and ready but does a better job than nothing.
2014-03-07 20:05:58 +00:00
Lucas Galfasó 53ec5e13e5 fix($compile): support templates with thead and tfoot root elements
If the first element in a template is a <thead> or a <tfoot>, then
use the existing logic to handle table elements compilation.

Closes #6289
2014-03-07 10:06:12 -08:00
Peter Bacon Darwin 235731d32b chore(package): update dgeni dependencies 2014-03-07 11:19:17 +00:00
Peter Bacon Darwin 5af8d2963b chore(doc-gen): add contentFolder config property 2014-03-07 11:19:17 +00:00
Peter Bacon Darwin 0b4a41af58 chore(doc-gen): add inline @type tag 2014-03-07 11:19:17 +00:00
Peter Bacon Darwin 0e066693f2 docs($route): fix formatting of example code 2014-03-07 11:19:17 +00:00
Peter Bacon Darwin 02cc2b2014 chore(doc-gen): fix error-doc processor
The meta-data should be parsed from the name not the id.
2014-03-07 10:41:48 +00:00
Peter Bacon Darwin 486f1b4e51 chore(doc-gen): improve error reporting 2014-03-07 10:41:48 +00:00
Eddie Hedges c5f2f583ab docs(tutorial): link update for Jasmine
Jasmine doesn't live at the replaced link anymore.
It has a link to click through, but I figured it would be better
to just go directly to the correct location.

Closes #6591
2014-03-07 08:40:32 +01:00
Misha Moroshko 186a68f8ff docs(guide/services): minor fixes 2014-03-06 15:38:59 -08:00
Ben Lesh 46bd6dc88d feat(input): support types date, time, datetime-local, month, week
On older browser that don't support the new HTML5 inputs
and display a text input instead, the user is required to enter
the data in the corresponding ISO format. The value in `ng-model`
will always be a date.

E2e tests contain a workaround to a bug in webdriver,
see https://github.com/angular/protractor/issues/562.

Also adds weeks as format to the `dateFilter`.

Related to #757.
Closes #5864.
2014-03-06 12:21:15 -08:00
Sekib Omazic 0609453e1f fix(style): expressions in style tags
Enable data-binding for style tags.

Note: this feature does not work on IE8.

Closes #2387
Closes #6492
2014-03-06 02:19:30 -08:00
Igor Minar 7682e5747a chore(build): don't instruct Jenkins test on IE
for an unknown reason the VMs can't connect to local karma, so all builds on Jenkins (ci.angularjs.org)
are failing right now.

Since we want to kill Jenkins anyway, and travis tests on IE, this should not have any
significant impact on us.
2014-03-06 02:15:24 -08:00
Igor Minar eaa1d00b24 chore(build): remove IE8 target from all test configs
BREAKING CHANGE: As communicated before, IE8 is no longer supported.

more info: http://blog.angularjs.org/2013/12/angularjs-13-new-release-approaches.html
2014-03-06 01:53:17 -08:00
Chirayu Krishnappa 3cf2da0e38 chore(publish.sh): publish to all serving backends 2014-03-05 21:49:28 -08:00
Timothée Jeannin 9335378602 style: enable jscs requireLeftStickedOperators rule
Closed #6544.
2014-03-05 16:30:51 -08:00
Tony Bergeron de2ecb8a96 docs(directive.ngdoc): typo fix 2014-03-05 16:15:52 -08:00
tpiere 66fdc03642 docs(tutorial): update step_09.ngdoc
Closes #5991
2014-03-05 22:49:06 +01:00
Zak Johnson 8e2e9adb46 docs(guide/services): clean up typos 2014-03-05 13:29:24 -08:00
mgerstenblatt 7d604975a7 docs(guide/forms): fix a typo
Closes #6556
2014-03-05 22:10:57 +01:00
Chung-Min Cheng 02075dcf13 docs(tutorial): update step_08.ngdoc
Closes #6537
2014-03-05 21:48:22 +01:00
Sharon DiOrio 7c73bc916e docs(tutorial/index): improve accessibility
- Adds accessibility attributes to links and images.
- Adds a note on using NVM for node.
2014-03-05 12:22:53 -08:00
Takashi Nakagawa 2036fb1e71 chore(grunt): remove unnecessary white spaces 2014-03-05 12:08:43 -08:00
74 changed files with 6425 additions and 490 deletions
+2 -1
View File
@@ -1,5 +1,6 @@
{
"disallowKeywords": ["with"],
"disallowTrailingWhitespace": true,
"requireRightStickedOperators": ["!"]
"requireRightStickedOperators": ["!"],
"requireLeftStickedOperators": [","]
}
-1
View File
@@ -8,7 +8,6 @@
"requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"],
"disallowLeftStickedOperators": ["?", "+", "-", "/", "*", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
"disallowRightStickedOperators": ["?", "+", "/", "*", ":", "=", "==", "===", "!=", "!==", ">", ">=", "<", "<="],
"requireLeftStickedOperators": [","],
"disallowImplicitTypeConversion": ["string"],
"disallowMultipleLineBreaks": true,
"disallowKeywordsOnNewLine": ["else"],
+58
View File
@@ -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
View File
@@ -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();
+6 -3
View File
@@ -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) {
+11
View File
@@ -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>
+9 -6
View File
@@ -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,
+1 -1
View File
@@ -87,7 +87,7 @@ angular.module('DocsController', [])
breadcrumbPath += '/';
});
} else {
$scope.currentArea = null;
$scope.currentArea = NG_NAVIGATION['api'];
$scope.breadcrumb = [];
}
});
+4
View File
@@ -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;
+4 -4
View File
@@ -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));
}
};
})
+4
View File
@@ -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', [
+12
View File
@@ -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>';
};
}
};
+6
View File
@@ -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
+4 -3
View File
@@ -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) {
+3 -6
View File
@@ -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>
+1 -1
View File
@@ -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:
+2 -2
View File
@@ -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'`:
+18 -20
View File
@@ -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">
+5 -5
View File
@@ -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:
+1 -1
View File
@@ -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
+7 -7
View File
@@ -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
+1 -1
View File
@@ -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
+36 -27
View File
@@ -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>
+1 -1
View File
@@ -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.
+1 -1
View File
@@ -107,7 +107,7 @@ __`app/partials/phone-detail.html`:__
</dl>
</li>
...
</li>
<li>
<span>Additional Features</span>
<dd>{{phone.additionalFeatures}}</dd>
</li>
+2 -2
View File
@@ -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']).
...
```
+1 -1
View File
@@ -83,7 +83,7 @@ __`app/js/app.js`.__
```js
// ...
angular.module('phonecat', [
angular.module('phonecatApp', [
'ngRoute',
'phonecatAnimations',
+9 -7
View File
@@ -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
View File
@@ -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
View File
@@ -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 #
-13
View File
@@ -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
View File
@@ -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() + ' ' +
+178
View File
@@ -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();
+4314
View File
File diff suppressed because it is too large Load Diff
+7 -6
View File
@@ -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": [
{
+26 -8
View File
@@ -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
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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)
}
+3
View File
@@ -101,6 +101,9 @@
"getter": false,
"getBlockElements": false,
/* filters.js */
"getFirstThursdayOfYear": false,
/* AngularPublic.js */
"version": false,
"publishExternalAPI": false,
+1 -1
View File
@@ -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');
-2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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}
*
+1 -1
View File
@@ -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.
*
-2
View File
@@ -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.
+5 -3
View File
@@ -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
-1
View File
@@ -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
+1 -1
View File
@@ -2,5 +2,5 @@
var styleDirective = valueFn({
restrict: 'E',
terminal: true
terminal: false
});
+33 -3
View File
@@ -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>
+5 -3
View File
@@ -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
View File
@@ -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;
}];
-2
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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. |
*
+3 -4
View File
@@ -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() {
+2 -2
View File
@@ -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.)
*/
/**
+11 -1
View File
@@ -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
View File
@@ -82,7 +82,7 @@ function $RouteProvider(){
*
* If `template` is a function, it will be called with the following parameters:
*
* - `{Array.&lt;Object&gt;}` - 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.&lt;Object&gt;}` - 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>
*/
/**
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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) {}
*/
+48
View File
@@ -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);
}));
});
+712 -1
View File
@@ -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" />');
+55 -3
View File
@@ -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 }}');
}));
+7 -1
View File
@@ -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');
+24 -6
View File
@@ -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
};
+32
View File
@@ -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) {
+12 -8
View File
@@ -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
View File
@@ -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');
}));
+43
View File
@@ -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) {
+2 -2
View File
@@ -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');
+2 -2
View File
@@ -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');