Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 07d62426f6 | |||
| 3a142e79bd | |||
| b40130abcf | |||
| ee988d40d0 | |||
| ad4d26c525 | |||
| f58681564d | |||
| d23f585f4a | |||
| 1850f59765 | |||
| 1f182853d4 | |||
| 5036ac905b | |||
| 11d2242df6 | |||
| cea23db3de | |||
| 5c9c197305 | |||
| ecb2222ea1 | |||
| 276310e5b5 | |||
| 6afe0dba39 | |||
| 038ca9b9bf | |||
| 44c451e9f5 | |||
| 1b8b41ad23 | |||
| 37f2265b88 | |||
| 05a2a1d395 | |||
| fc913eea89 | |||
| f839e4944a | |||
| 2aead39486 | |||
| 54af406c2e | |||
| 930bd40bac | |||
| 4aa4005f5f | |||
| d5c04121cb | |||
| e6996cc457 | |||
| 33793ec3e1 | |||
| c9f8fb2559 | |||
| a0229803de | |||
| 38cb2b348e | |||
| c54287bf8d | |||
| a36515289a | |||
| c59da09d3e | |||
| 98ceda2194 | |||
| fbd620e8ae | |||
| 4cfc279d39 | |||
| 6a07009c12 | |||
| f3f65caac2 | |||
| 130fe92cda | |||
| 3d83973fa4 | |||
| 3b171a851d | |||
| 9782bc2aa2 | |||
| 77be7cc7d9 | |||
| 3da5847500 | |||
| 53e6590e88 | |||
| d1c05ad832 |
@@ -11,7 +11,6 @@ env:
|
||||
- JOB=unit
|
||||
- JOB=e2e TEST_TARGET=jqlite
|
||||
- JOB=e2e TEST_TARGET=jquery
|
||||
- JOB=e2e TEST_TARGET=doce2e
|
||||
global:
|
||||
- SAUCE_USERNAME=angular-ci
|
||||
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||
|
||||
+178
@@ -1,3 +1,181 @@
|
||||
<a name="1.3.0-rc.4"></a>
|
||||
# 1.3.0-rc.4 unicorn-hydrafication (2014-10-01)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **$compile:**
|
||||
- get $$observe listeners array as own property
|
||||
([a27d827c](https://github.com/angular/angular.js/commit/a27d827c22b0b6b3ba6b7495cf4fc338c6934b37),
|
||||
[#9343](https://github.com/angular/angular.js/issues/9343), [#9345](https://github.com/angular/angular.js/issues/9345))
|
||||
- Resolve leak with asynchronous compilation
|
||||
([6303c3dc](https://github.com/angular/angular.js/commit/6303c3dcf64685458fc84aa12289f5c9d57f4e47),
|
||||
[#9199](https://github.com/angular/angular.js/issues/9199), [#9079](https://github.com/angular/angular.js/issues/9079), [#8504](https://github.com/angular/angular.js/issues/8504), [#9197](https://github.com/angular/angular.js/issues/9197))
|
||||
- connect transclude scopes to their containing scope to prevent memory leaks
|
||||
([fb0c77f0](https://github.com/angular/angular.js/commit/fb0c77f0b66ed757a56af13f81b943419fdcbd7f),
|
||||
[#9095](https://github.com/angular/angular.js/issues/9095), [#9281](https://github.com/angular/angular.js/issues/9281))
|
||||
- sanitize srcset attribute
|
||||
([ab80cd90](https://github.com/angular/angular.js/commit/ab80cd90661396dbb1c94c5f4dd2d11ee8f6b6af))
|
||||
- **input:**
|
||||
- register builtin parsers/formatters before anyone else
|
||||
([10644432](https://github.com/angular/angular.js/commit/10644432ca9d5da69ce790a8d9e691640f333711),
|
||||
[#9218](https://github.com/angular/angular.js/issues/9218), [#9358](https://github.com/angular/angular.js/issues/9358))
|
||||
- correctly handle invalid model values for `input[date/time/…]`
|
||||
([a0bfdd0d](https://github.com/angular/angular.js/commit/a0bfdd0d60882125f614a91c321f12f730735e7b),
|
||||
[#8949](https://github.com/angular/angular.js/issues/8949), [#9375](https://github.com/angular/angular.js/issues/9375))
|
||||
- **ngModel:** do not parse undefined viewValue when validating
|
||||
([92f05e5a](https://github.com/angular/angular.js/commit/92f05e5a5900713301e64373d7b7daa45a88278b),
|
||||
[#9106](https://github.com/angular/angular.js/issues/9106), [#9260](https://github.com/angular/angular.js/issues/9260))
|
||||
- **ngView:** use animation promises ensure that only one leave animation occurs at a time
|
||||
([3624e380](https://github.com/angular/angular.js/commit/3624e3800fb3ccd2e9ea361a763e20131fd42c29),
|
||||
[#9355](https://github.com/angular/angular.js/issues/9355), [#7606](https://github.com/angular/angular.js/issues/7606), [#9374](https://github.com/angular/angular.js/issues/9374))
|
||||
- **select:** make ctrl.hasOption method consistent
|
||||
([2bcd02dc](https://github.com/angular/angular.js/commit/2bcd02dc1a6b28b357d47c83be3bed5c9a38417c),
|
||||
[#8761](https://github.com/angular/angular.js/issues/8761))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$compile:** optionally get controllers from ancestors only
|
||||
([07e3abc7](https://github.com/angular/angular.js/commit/07e3abc7dda872adc3fb25cb3e133f86f494b35d),
|
||||
[#4518](https://github.com/angular/angular.js/issues/4518), [#4540](https://github.com/angular/angular.js/issues/4540), [#8240](https://github.com/angular/angular.js/issues/8240), [#8511](https://github.com/angular/angular.js/issues/8511))
|
||||
- **Scope:** allow the parent of a new scope to be specified on creation
|
||||
([6417a3e9](https://github.com/angular/angular.js/commit/6417a3e9eb7ab0011cefada8db855aa929a64ff8))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **$rootScope:** moving internal queues out of the Scope instances
|
||||
([b1192518](https://github.com/angular/angular.js/commit/b119251827cea670051198e1b48af7ee0c9f2a1b),
|
||||
[#9071](https://github.com/angular/angular.js/issues/9071))
|
||||
- **benchmark:** add ngBindOnce benchmarks to largetable-bp
|
||||
([2c8b4648](https://github.com/angular/angular.js/commit/2c8b4648526acf5c2645de8408a6d9ace2144b5f))
|
||||
- **ngForm,ngModel:** move initial addClass to the compile phase
|
||||
([b1ee5386](https://github.com/angular/angular.js/commit/b1ee5386d584f208bce6d3b613afdb3bae9df76a),
|
||||
[#8268](https://github.com/angular/angular.js/issues/8268))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$compile:** due to [fb0c77f0](https://github.com/angular/angular.js/commit/fb0c77f0b66ed757a56af13f81b943419fdcbd7f),
|
||||
|
||||
|
||||
`$transclude` functions no longer attach `$destroy` event handlers to the
|
||||
transcluded content, and so the associated transclude scope will not automatically
|
||||
be destroyed if you remove a transcluded element from the DOM using direct DOM
|
||||
manipulation such as the jquery `remove()` method.
|
||||
|
||||
If you want to explicitly remove DOM elements inside your directive that have
|
||||
been compiled, and so potentially contain child (and transcluded) scopes, then
|
||||
it is your responsibility to get hold of the scope and destroy it at the same time.
|
||||
|
||||
The suggested approach is to create a new child scope of your own around any DOM
|
||||
elements that you wish to manipulate in this way and destroy those scopes if you
|
||||
remove their contents - any child scopes will then be destroyed and cleaned up
|
||||
automatically.
|
||||
|
||||
Note that all the built-in directives that manipulate the DOM (ngIf, ngRepeat,
|
||||
ngSwitch, etc) already follow this best practice, so if you only use these for
|
||||
manipulating the DOM then you do not have to worry about this change.
|
||||
|
||||
Closes #9095
|
||||
Closes #9281
|
||||
|
||||
- **$parse:** due to [5572b40b](https://github.com/angular/angular.js/commit/5572b40b15ed06969c8e0e92866c5afd088484b4),
|
||||
|
||||
- $scope['this'] no longer exits on the $scope object
|
||||
- $parse-ed expressions no longer allow chaining 'this' such as this['this'] or $parent['this']
|
||||
- 'this' in $parse-ed expressions can no longer be overriden, if a variable named 'this' is put on the scope it must be accessed using this['this']
|
||||
|
||||
Closes #9105
|
||||
|
||||
- **input:** due to [1eda1836](https://github.com/angular/angular.js/commit/1eda18365a348c9597aafba9d195d345e4f13d1e),
|
||||
|
||||
(Note: this change landed in 1.3.0-rc.3, but was not considered a breaking change at the time).
|
||||
|
||||
For text based inputs (text, email, url), the `$viewValue` will now always be converted to a string,
|
||||
regardless of what type the value is on the model.
|
||||
|
||||
To migrate, any code or expressions that expect the `$viewValue` to be anything other than string
|
||||
should be updated to expect a string.
|
||||
|
||||
|
||||
- **input:** due to a0bfdd0d60882125f614a91c321f12f730735e7b (see #8949),
|
||||
|
||||
Similar to `input[number]` Angular will now throw if the model value
|
||||
for a `input[date]` is not a `Date` object. Previously, Angular only
|
||||
showed an empty string instead.
|
||||
Angular does not set validation errors on the `<input>` in this case
|
||||
as those errors are shown to the user, but the erroneous state was
|
||||
caused by incorrect application logic and not by the user.
|
||||
|
||||
<a name="1.2.26"></a>
|
||||
# 1.2.26 captivating-disinterest (2014-10-01)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
|
||||
- **$compile:** Resolve leak with asynchronous compilation
|
||||
([5c9c1973](https://github.com/angular/angular.js/commit/5c9c19730526d5df6f16c523e578e5305f3796d0),
|
||||
[#9199](https://github.com/angular/angular.js/issues/9199), [#9079](https://github.com/angular/angular.js/issues/9079), [#8504](https://github.com/angular/angular.js/issues/8504), [#9197](https://github.com/angular/angular.js/issues/9197))
|
||||
- **select:** make ctrl.hasOption method consistent
|
||||
([11d2242d](https://github.com/angular/angular.js/commit/11d2242df65b2ade0dabe366a0c42963b6d37df5),
|
||||
[#8761](https://github.com/angular/angular.js/issues/8761))
|
||||
|
||||
|
||||
<a name="1.3.0-rc.3"></a>
|
||||
# 1.3.0-rc.3 aggressive-pacification (2014-09-23)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **ngModel:** support milliseconds in time and datetime
|
||||
([4b83f6ca](https://github.com/angular/angular.js/commit/4b83f6ca2c15bd65fe2b3894a02c04f9967fbff4),
|
||||
[#8874](https://github.com/angular/angular.js/issues/8874))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **$location:** add ability to opt-out of `<base>` tag requirement in html5Mode
|
||||
([dc3de7fb](https://github.com/angular/angular.js/commit/dc3de7fb7a14c38b5c3dc7decfafb0b51d422dd1),
|
||||
[#8934](https://github.com/angular/angular.js/issues/8934))
|
||||
- **formController:** add $setUntouched to propagate untouched state
|
||||
([fd899755](https://github.com/angular/angular.js/commit/fd8997551f9ed4431f5e99d61f637139485076b9),
|
||||
[#9050](https://github.com/angular/angular.js/issues/9050))
|
||||
- **input:** support dynamic element validation
|
||||
([729c238e](https://github.com/angular/angular.js/commit/729c238e19ab27deff01448d79342ea53721bfed),
|
||||
[#4791](https://github.com/angular/angular.js/issues/4791), [#1404](https://github.com/angular/angular.js/issues/1404))
|
||||
- **ngAria:** add an ngAria module to make a11y easier
|
||||
([d1434c99](https://github.com/angular/angular.js/commit/d1434c999a66c6bb915ee1a8b091e497d288d940),
|
||||
[#5486](https://github.com/angular/angular.js/issues/5486))
|
||||
|
||||
|
||||
## Performance Improvements
|
||||
|
||||
- **map:** use Array.prototype.map
|
||||
([a591e8b8](https://github.com/angular/angular.js/commit/a591e8b8d302efefd67bf0d5c4bad300a5f3aded))
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- **$location:** due to [dc3de7fb](https://github.com/angular/angular.js/commit/dc3de7fb7a14c38b5c3dc7decfafb0b51d422dd1),
|
||||
The $location.html5Mode API has changed to allow enabling html5Mode by
|
||||
passing an object (as well as still supporting passing a boolean). Symmetrically, the
|
||||
method now returns an object instead of a boolean value.
|
||||
|
||||
To migrate, follow the code example below:
|
||||
|
||||
Before:
|
||||
|
||||
var mode = $locationProvider.html5Mode();
|
||||
|
||||
After:
|
||||
|
||||
var mode = $locationProvider.html5Mode().enabled;
|
||||
|
||||
Fixes #8934
|
||||
|
||||
|
||||
<a name="1.2.25"></a>
|
||||
# 1.2.25 hypnotic-gesticulation (2014-09-16)
|
||||
|
||||
|
||||
+3
-3
@@ -65,13 +65,13 @@ Help us to maximize the effort we can spend fixing issues and adding new
|
||||
features, by not reporting duplicate issues. Providing the following information will increase the
|
||||
chances of your issue being dealt with quickly:
|
||||
|
||||
* **Overview of the issue** - if an error is being thrown a non-minified stack trace helps
|
||||
* **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
|
||||
* **Motivation for or Use Case** - explain why this is a bug for you
|
||||
* **Angular Version(s)** - is it a regression?
|
||||
* **Browsers and Operating System** - is this a problem with all browsers or only IE8?
|
||||
* **Reproduce the error** - provide a live example (using [Plunker][plunker] or
|
||||
* **Reproduce the Error** - provide a live example (using [Plunker][plunker] or
|
||||
[JSFiddle][jsfiddle]) or a unambiguous set of steps.
|
||||
* **Related issues** - has a similar issue been reported before?
|
||||
* **Related Issues** - has a similar issue been reported before?
|
||||
* **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
|
||||
causing the problem (line of code or commit)
|
||||
|
||||
|
||||
@@ -6,10 +6,11 @@ use good old HTML (or HAML, Jade and friends!) as your template language and let
|
||||
syntax to express your application’s components clearly and succinctly. It automatically
|
||||
synchronizes data from your UI (view) with your JavaScript objects (model) through 2-way data
|
||||
binding. To help you structure your application better and make it easy to test, AngularJS teaches
|
||||
the browser how to do dependency injection and inversion of control. Oh yeah and it also helps with
|
||||
server-side communication, taming async callbacks with promises and deferreds; and makes client-side
|
||||
navigation and deeplinking with hashbang urls or HTML5 pushState a piece of cake. The best of all:
|
||||
it makes development fun!
|
||||
the browser how to do dependency injection and inversion of control.
|
||||
|
||||
Oh yeah and it helps with server-side communication, taming async callbacks with promises and
|
||||
deferreds. It also makes client-side navigation and deeplinking with hashbang urls or HTML5 pushState a
|
||||
piece of cake. The best of all: it makes development fun!
|
||||
|
||||
* Web site: http://angularjs.org
|
||||
* Tutorial: http://docs.angularjs.org/tutorial
|
||||
|
||||
@@ -26,7 +26,6 @@ This process based on the idea of minimizing user pain
|
||||
* You can triage older issues as well
|
||||
* Triage to your heart's content
|
||||
1. Assign yourself: Pick an issue that is not assigned to anyone and assign it to you
|
||||
|
||||
1. Understandable? - verify if the description of the request is clear.
|
||||
* If not, [close it][] according to the instructions below and go to the last step.
|
||||
1. Duplicate?
|
||||
@@ -36,7 +35,6 @@ This process based on the idea of minimizing user pain
|
||||
* Label `Type: Bug`
|
||||
* Reproducible? - Steps to reproduce the bug are clear. If they are not, ask for a clarification. If there's no reply after a week, [close it][].
|
||||
* Reproducible on master? - <http://code.angularjs.org/snapshot/>
|
||||
|
||||
1. Non bugs:
|
||||
* Label `Type: Feature`, `Type: Chore`, or `Type: Perf`
|
||||
* Belongs in core? – Often new features should be implemented as a third-party module rather than an addition to the core.
|
||||
@@ -59,7 +57,6 @@ This process based on the idea of minimizing user pain
|
||||
* In rare cases, it's ok to have multiple components.
|
||||
1. Label `PRs plz!` - These issues are good targets for PRs from the open source community. Apply to issues where the problem and solution are well defined in the comments, and it's not too complex.
|
||||
1. Label `origin: google` for issues from Google
|
||||
|
||||
1. Assign a milestone:
|
||||
* Backlog - triaged fixes and features, should be the default choice
|
||||
* Current 1.x.y milestone (e.g. 1.3.0-beta-2) - regressions and urgent bugs only
|
||||
|
||||
@@ -316,10 +316,10 @@ iframe.example {
|
||||
}
|
||||
|
||||
.search-results-group.col-group-api { width:30%; }
|
||||
.search-results-group.col-group-guide { width:30%; }
|
||||
.search-results-group.col-group-tutorial { width:25%; }
|
||||
.search-results-group.col-group-guide,
|
||||
.search-results-group.col-group-tutorial { width:20%; }
|
||||
.search-results-group.col-group-misc,
|
||||
.search-results-group.col-group-error { float:right; clear:both; width:15% }
|
||||
.search-results-group.col-group-error { width:15%; float: right; }
|
||||
|
||||
|
||||
.search-results-group.col-group-api .search-result {
|
||||
@@ -391,7 +391,6 @@ iframe.example {
|
||||
position:fixed;
|
||||
top:120px;
|
||||
bottom:0;
|
||||
padding-bottom:120px;
|
||||
overflow:auto;
|
||||
}
|
||||
|
||||
@@ -412,6 +411,7 @@ iframe.example {
|
||||
|
||||
.main-body-grid .side-navigation {
|
||||
position:relative;
|
||||
padding-bottom:120px;
|
||||
}
|
||||
|
||||
.main-body-grid .side-navigation.ng-hide {
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
var directive = {};
|
||||
var service = { value: {} };
|
||||
|
||||
var DEPENDENCIES = {
|
||||
'angular.js': 'http://code.angularjs.org/' + angular.version.full + '/angular.min.js',
|
||||
'angular-resource.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-resource.min.js',
|
||||
'angular-route.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-route.min.js',
|
||||
'angular-animate.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-animate.min.js',
|
||||
'angular-sanitize.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-sanitize.min.js',
|
||||
'angular-cookies.js': 'http://code.angularjs.org/' + angular.version.full + '/angular-cookies.min.js'
|
||||
};
|
||||
|
||||
|
||||
function escape(text) {
|
||||
return text.
|
||||
replace(/\&/g, '&').
|
||||
replace(/\</g, '<').
|
||||
replace(/\>/g, '>').
|
||||
replace(/"/g, '"');
|
||||
}
|
||||
|
||||
/**
|
||||
* http://stackoverflow.com/questions/451486/pre-tag-loses-line-breaks-when-setting-innerhtml-in-ie
|
||||
* http://stackoverflow.com/questions/195363/inserting-a-newline-into-a-pre-tag-ie-javascript
|
||||
*/
|
||||
function setHtmlIe8SafeWay(element, html) {
|
||||
var newElement = angular.element('<pre>' + html + '</pre>');
|
||||
|
||||
element.empty();
|
||||
element.append(newElement.contents());
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
directive.jsFiddle = function(getEmbeddedTemplate, escape, script) {
|
||||
return {
|
||||
terminal: true,
|
||||
link: function(scope, element, attr) {
|
||||
var name = '',
|
||||
stylesheet = '<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">\n',
|
||||
fields = {
|
||||
html: '',
|
||||
css: '',
|
||||
js: ''
|
||||
};
|
||||
|
||||
angular.forEach(attr.jsFiddle.split(' '), function(file, index) {
|
||||
var fileType = file.split('.')[1];
|
||||
|
||||
if (fileType == 'html') {
|
||||
if (index == 0) {
|
||||
fields[fileType] +=
|
||||
'<div ng-app' + (attr.module ? '="' + attr.module + '"' : '') + '>\n' +
|
||||
getEmbeddedTemplate(file, 2);
|
||||
} else {
|
||||
fields[fileType] += '\n\n\n <!-- CACHE FILE: ' + file + ' -->\n' +
|
||||
' <script type="text/ng-template" id="' + file + '">\n' +
|
||||
getEmbeddedTemplate(file, 4) +
|
||||
' </script>\n';
|
||||
}
|
||||
} else {
|
||||
fields[fileType] += getEmbeddedTemplate(file) + '\n';
|
||||
}
|
||||
});
|
||||
|
||||
fields.html += '</div>\n';
|
||||
|
||||
setHtmlIe8SafeWay(element,
|
||||
'<form class="jsfiddle" method="post" action="http://jsfiddle.net/api/post/library/pure/" target="_blank">' +
|
||||
hiddenField('title', 'AngularJS Example: ' + name) +
|
||||
hiddenField('css', '</style> <!-- Ugly Hack due to jsFiddle issue: http://goo.gl/BUfGZ --> \n' +
|
||||
stylesheet +
|
||||
script.angular +
|
||||
(attr.resource ? script.resource : '') +
|
||||
'<style>\n' +
|
||||
fields.css) +
|
||||
hiddenField('html', fields.html) +
|
||||
hiddenField('js', fields.js) +
|
||||
'<button class="btn btn-primary"><i class="icon-white icon-pencil"></i> Edit Me</button>' +
|
||||
'</form>');
|
||||
|
||||
function hiddenField(name, value) {
|
||||
return '<input type="hidden" name="' + name + '" value="' + escape(value) + '">';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
directive.ngSetText = ['getEmbeddedTemplate', function(getEmbeddedTemplate) {
|
||||
return {
|
||||
restrict: 'CA',
|
||||
priority: 10,
|
||||
compile: function(element, attr) {
|
||||
setHtmlIe8SafeWay(element, escape(getEmbeddedTemplate(attr.ngSetText)));
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
|
||||
directive.ngHtmlWrap = ['reindentCode', 'templateMerge', function(reindentCode, templateMerge) {
|
||||
return {
|
||||
compile: function(element, attr) {
|
||||
var properties = {
|
||||
head: '',
|
||||
module: '',
|
||||
body: element.text()
|
||||
},
|
||||
html = "<!doctype html>\n<html ng-app{{module}}>\n <head>\n{{head:4}} </head>\n <body>\n{{body:4}} </body>\n</html>";
|
||||
|
||||
angular.forEach((attr.ngHtmlWrap || '').split(' '), function(dep) {
|
||||
if (!dep) return;
|
||||
dep = DEPENDENCIES[dep] || dep;
|
||||
|
||||
var ext = dep.split(/\./).pop();
|
||||
|
||||
if (ext == 'css') {
|
||||
properties.head += '<link rel="stylesheet" href="' + dep + '" type="text/css">\n';
|
||||
} else if(ext == 'js') {
|
||||
properties.head += '<script src="' + dep + '"></script>\n';
|
||||
} else {
|
||||
properties.module = '="' + dep + '"';
|
||||
}
|
||||
});
|
||||
|
||||
setHtmlIe8SafeWay(element, escape(templateMerge(html, properties)));
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
directive.ngSetHtml = ['getEmbeddedTemplate', function(getEmbeddedTemplate) {
|
||||
return {
|
||||
restrict: 'CA',
|
||||
priority: 10,
|
||||
compile: function(element, attr) {
|
||||
setHtmlIe8SafeWay(element, getEmbeddedTemplate(attr.ngSetHtml));
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
directive.ngEvalJavascript = ['getEmbeddedTemplate', function(getEmbeddedTemplate) {
|
||||
return {
|
||||
compile: function (element, attr) {
|
||||
var fileNames = attr.ngEvalJavascript.split(' ');
|
||||
angular.forEach(fileNames, function(fileName) {
|
||||
var script = getEmbeddedTemplate(fileName);
|
||||
try {
|
||||
if (window.execScript) { // IE
|
||||
window.execScript(script || '""'); // IE complains when evaling empty string
|
||||
} else {
|
||||
window.eval(script + '//@ sourceURL=' + fileName);
|
||||
}
|
||||
} catch (e) {
|
||||
if (window.console) {
|
||||
window.console.log(script, '\n', e);
|
||||
} else {
|
||||
window.alert(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
|
||||
directive.ngEmbedApp = ['$templateCache', '$browser', '$rootScope', '$location', '$sniffer', '$animate',
|
||||
function($templateCache, $browser, docsRootScope, $location, $sniffer, $animate) {
|
||||
return {
|
||||
terminal: true,
|
||||
link: function(scope, element, attrs) {
|
||||
var modules = ['ngAnimate'],
|
||||
embedRootScope,
|
||||
deregisterEmbedRootScope;
|
||||
|
||||
modules.push(['$provide', function($provide) {
|
||||
$provide.value('$templateCache', $templateCache);
|
||||
$provide.value('$anchorScroll', angular.noop);
|
||||
$provide.value('$browser', $browser);
|
||||
$provide.value('$sniffer', $sniffer);
|
||||
$provide.value('$animate', $animate);
|
||||
$provide.provider('$location', function() {
|
||||
this.$get = ['$rootScope', function($rootScope) {
|
||||
docsRootScope.$on('$locationChangeSuccess', function(event, oldUrl, newUrl) {
|
||||
$rootScope.$broadcast('$locationChangeSuccess', oldUrl, newUrl);
|
||||
});
|
||||
return $location;
|
||||
}];
|
||||
this.html5Mode = angular.noop;
|
||||
});
|
||||
|
||||
$provide.decorator('$rootScope', ['$delegate', function($delegate) {
|
||||
embedRootScope = $delegate;
|
||||
|
||||
// Since we are teleporting the $animate service, which relies on the $$postDigestQueue
|
||||
// we need the embedded scope to use the same $$postDigestQueue as the outer scope
|
||||
embedRootScope.$$postDigestQueue = docsRootScope.$$postDigestQueue;
|
||||
|
||||
deregisterEmbedRootScope = docsRootScope.$watch(function embedRootScopeDigestWatch() {
|
||||
embedRootScope.$digest();
|
||||
});
|
||||
|
||||
return embedRootScope;
|
||||
}]);
|
||||
}]);
|
||||
if (attrs.ngEmbedApp) modules.push(attrs.ngEmbedApp);
|
||||
|
||||
element.on('click', function(event) {
|
||||
if (event.target.attributes.getNamedItem('ng-click')) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
element.bind('$destroy', function() {
|
||||
deregisterEmbedRootScope();
|
||||
embedRootScope.$destroy();
|
||||
});
|
||||
|
||||
element.data('$injector', null);
|
||||
angular.bootstrap(element, modules);
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
||||
service.reindentCode = function() {
|
||||
return function (text, spaces) {
|
||||
if (!text) return text;
|
||||
var lines = text.split(/\r?\n/);
|
||||
var prefix = ' '.substr(0, spaces || 0);
|
||||
var i;
|
||||
|
||||
// remove any leading blank lines
|
||||
while (lines.length && lines[0].match(/^\s*$/)) lines.shift();
|
||||
// remove any trailing blank lines
|
||||
while (lines.length && lines[lines.length - 1].match(/^\s*$/)) lines.pop();
|
||||
var minIndent = 999;
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
var line = lines[0];
|
||||
var reindentCode = line.match(/^\s*/)[0];
|
||||
if (reindentCode !== line && reindentCode.length < minIndent) {
|
||||
minIndent = reindentCode.length;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < lines.length; i++) {
|
||||
lines[i] = prefix + lines[i].substring(minIndent);
|
||||
}
|
||||
lines.push('');
|
||||
return lines.join('\n');
|
||||
}
|
||||
};
|
||||
|
||||
service.templateMerge = ['reindentCode', function(indentCode) {
|
||||
return function(template, properties) {
|
||||
return template.replace(/\{\{(\w+)(?:\:(\d+))?\}\}/g, function(_, key, indent) {
|
||||
var value = properties[key];
|
||||
|
||||
if (indent) {
|
||||
value = indentCode(value, indent);
|
||||
}
|
||||
|
||||
return value == undefined ? '' : value;
|
||||
});
|
||||
};
|
||||
}];
|
||||
|
||||
service.getEmbeddedTemplate = ['reindentCode', function(reindentCode) {
|
||||
return function (id) {
|
||||
var element = document.getElementById(id);
|
||||
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return reindentCode(angular.element(element).html(), 0);
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
angular.module('bootstrapPrettify', []).directive(directive).factory(service);
|
||||
@@ -0,0 +1,44 @@
|
||||
"use strict";
|
||||
/* jshint browser: true */
|
||||
/* global importScripts, onmessage: true, postMessage, lunr */
|
||||
|
||||
// Load up the lunr library
|
||||
importScripts('../components/lunr.js-0.4.2/lunr.min.js');
|
||||
|
||||
// Create the lunr index - the docs should be an array of object, each object containing
|
||||
// the path and search terms for a page
|
||||
var index = lunr(function() {
|
||||
this.ref('path');
|
||||
this.field('titleWords', {boost: 50});
|
||||
this.field('members', { boost: 40});
|
||||
this.field('keywords', { boost : 20 });
|
||||
});
|
||||
|
||||
// Retrieve the searchData which contains the information about each page to be indexed
|
||||
var searchData = {};
|
||||
var searchDataRequest = new XMLHttpRequest();
|
||||
searchDataRequest.onload = function() {
|
||||
|
||||
// Store the pages data to be used in mapping query results back to pages
|
||||
searchData = JSON.parse(this.responseText);
|
||||
// Add search terms from each page to the search index
|
||||
searchData.forEach(function(page) {
|
||||
index.add(page);
|
||||
});
|
||||
postMessage({ e: 'index-ready' });
|
||||
};
|
||||
searchDataRequest.open('GET', 'search-data.json');
|
||||
searchDataRequest.send();
|
||||
|
||||
// The worker receives a message everytime the web app wants to query the index
|
||||
onmessage = function(oEvent) {
|
||||
var q = oEvent.data.q;
|
||||
var hits = index.search(q);
|
||||
var results = [];
|
||||
// Only return the array of paths to pages
|
||||
hits.forEach(function(hit) {
|
||||
results.push(hit.ref);
|
||||
});
|
||||
// The results of the query are sent back to the web app via a new message
|
||||
postMessage({ e: 'query-ready', q: q, d: results });
|
||||
};
|
||||
@@ -54,10 +54,13 @@ describe('docs.angularjs.org', function () {
|
||||
});
|
||||
|
||||
|
||||
it("should display an error if the page does not exist", function() {
|
||||
browser.get('index-debug.html#!/api/does/not/exist');
|
||||
expect(element(by.css('h1')).getText()).toBe('Oops!');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Error Handling', function() {
|
||||
it("should display an error if the page does not exist", function() {
|
||||
browser.get('index-debug.html#!/api/does/not/exist');
|
||||
expect(element(by.css('h1')).getText()).toBe('Oops!');
|
||||
});
|
||||
});
|
||||
|
||||
+3
-3
@@ -6,6 +6,7 @@ angular.module('docsApp', [
|
||||
'DocsController',
|
||||
'versionsData',
|
||||
'pagesData',
|
||||
'navData',
|
||||
'directives',
|
||||
'errors',
|
||||
'examples',
|
||||
@@ -13,11 +14,10 @@ angular.module('docsApp', [
|
||||
'tutorials',
|
||||
'versions',
|
||||
'bootstrap',
|
||||
'bootstrapPrettify',
|
||||
'ui.bootstrap.dropdown'
|
||||
])
|
||||
|
||||
|
||||
.config(function($locationProvider) {
|
||||
.config(['$locationProvider', function($locationProvider) {
|
||||
$locationProvider.html5Mode(true).hashPrefix('!');
|
||||
});
|
||||
}]);
|
||||
|
||||
+9
-74
@@ -6,31 +6,10 @@ angular.module('DocsController', [])
|
||||
function($scope, $rootScope, $location, $window, $cookies, openPlunkr,
|
||||
NG_PAGES, NG_NAVIGATION, NG_VERSION) {
|
||||
|
||||
|
||||
$scope.openPlunkr = openPlunkr;
|
||||
|
||||
$scope.docsVersion = NG_VERSION.isSnapshot ? 'snapshot' : NG_VERSION.version;
|
||||
|
||||
$scope.fold = function(url) {
|
||||
if(url) {
|
||||
$scope.docs_fold = '/notes/' + url;
|
||||
if(/\/build/.test($window.location.href)) {
|
||||
$scope.docs_fold = '/build/docs' + $scope.docs_fold;
|
||||
}
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
else {
|
||||
$scope.docs_fold = null;
|
||||
}
|
||||
};
|
||||
var OFFLINE_COOKIE_NAME = 'ng-offline',
|
||||
INDEX_PATH = /^(\/|\/index[^\.]*.html)$/;
|
||||
|
||||
|
||||
/**********************************
|
||||
Publish methods
|
||||
***********************************/
|
||||
|
||||
$scope.navClass = function(navItem) {
|
||||
return {
|
||||
active: navItem.href && this.currentPage.path,
|
||||
@@ -38,55 +17,22 @@ angular.module('DocsController', [])
|
||||
};
|
||||
};
|
||||
|
||||
$scope.afterPartialLoaded = function() {
|
||||
|
||||
|
||||
$scope.$on('$includeContentLoaded', function() {
|
||||
var pagePath = $scope.currentPage ? $scope.currentPage.path : $location.path();
|
||||
$window._gaq.push(['_trackPageview', pagePath]);
|
||||
};
|
||||
|
||||
/** stores a cookie that is used by apache to decide which manifest ot send */
|
||||
$scope.enableOffline = function() {
|
||||
//The cookie will be good for one year!
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime()+(365*24*60*60*1000));
|
||||
var expires = "; expires="+date.toGMTString();
|
||||
var value = angular.version.full;
|
||||
document.cookie = OFFLINE_COOKIE_NAME + "="+value+expires+"; path=" + $location.path;
|
||||
|
||||
//force the page to reload so server can serve new manifest file
|
||||
window.location.reload(true);
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**********************************
|
||||
Watches
|
||||
***********************************/
|
||||
|
||||
});
|
||||
|
||||
$scope.$watch(function docsPathWatch() {return $location.path(); }, function docsPathWatchAction(path) {
|
||||
|
||||
var currentPage = $scope.currentPage = NG_PAGES[path];
|
||||
if ( !currentPage && path.charAt(0)==='/' ) {
|
||||
// Strip off leading slash
|
||||
path = path.substr(1);
|
||||
}
|
||||
|
||||
currentPage = $scope.currentPage = NG_PAGES[path];
|
||||
if ( !currentPage && path.charAt(path.length-1) === '/' && path.length > 1 ) {
|
||||
// Strip off trailing slash
|
||||
path = path.substr(0, path.length-1);
|
||||
}
|
||||
|
||||
currentPage = $scope.currentPage = NG_PAGES[path];
|
||||
if ( !currentPage && /\/index$/.test(path) ) {
|
||||
// Strip off index from the end
|
||||
path = path.substr(0, path.length - 6);
|
||||
}
|
||||
path = path.replace(/^\/?(.+?)(\/index)?\/?$/, '$1');
|
||||
|
||||
currentPage = $scope.currentPage = NG_PAGES[path];
|
||||
|
||||
if ( currentPage ) {
|
||||
$scope.currentArea = currentPage && NG_NAVIGATION[currentPage.area];
|
||||
$scope.partialPath = 'partials/' + path + '.html';
|
||||
$scope.currentArea = NG_NAVIGATION[currentPage.area];
|
||||
var pathParts = currentPage.path.split('/');
|
||||
var breadcrumb = $scope.breadcrumb = [];
|
||||
var breadcrumbPath = '';
|
||||
@@ -98,6 +44,7 @@ angular.module('DocsController', [])
|
||||
} else {
|
||||
$scope.currentArea = NG_NAVIGATION['api'];
|
||||
$scope.breadcrumb = [];
|
||||
$scope.partialPath = 'Error404.html';
|
||||
}
|
||||
});
|
||||
|
||||
@@ -107,24 +54,12 @@ angular.module('DocsController', [])
|
||||
|
||||
$scope.versionNumber = angular.version.full;
|
||||
$scope.version = angular.version.full + " " + angular.version.codeName;
|
||||
$scope.subpage = false;
|
||||
$scope.offlineEnabled = ($cookies[OFFLINE_COOKIE_NAME] == angular.version.full);
|
||||
$scope.futurePartialTitle = null;
|
||||
$scope.loading = 0;
|
||||
$scope.$cookies = $cookies;
|
||||
|
||||
$cookies.platformPreference = $cookies.platformPreference || 'gitUnix';
|
||||
|
||||
var INDEX_PATH = /^(\/|\/index[^\.]*.html)$/;
|
||||
if (!$location.path() || INDEX_PATH.test($location.path())) {
|
||||
$location.path('/api').replace();
|
||||
}
|
||||
|
||||
// bind escape to hash reset callback
|
||||
angular.element(window).on('keydown', function(e) {
|
||||
if (e.keyCode === 27) {
|
||||
$scope.$apply(function() {
|
||||
$scope.subpage = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}]);
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
angular.module('docsApp.navigationService', [])
|
||||
|
||||
.factory('navigationService', function($window) {
|
||||
var service = {
|
||||
currentPage: null,
|
||||
currentVersion: null,
|
||||
changePage: function(newPage) {
|
||||
|
||||
},
|
||||
changeVersion: function(newVersion) {
|
||||
|
||||
//TODO =========
|
||||
// var currentPagePath = '';
|
||||
|
||||
// // preserve URL path when switching between doc versions
|
||||
// if (angular.isObject($rootScope.currentPage) && $rootScope.currentPage.section && $rootScope.currentPage.id) {
|
||||
// currentPagePath = '/' + $rootScope.currentPage.section + '/' + $rootScope.currentPage.id;
|
||||
// }
|
||||
|
||||
// $window.location = version.url + currentPagePath;
|
||||
|
||||
}
|
||||
};
|
||||
});
|
||||
+123
-64
@@ -10,22 +10,35 @@ angular.module('search', [])
|
||||
$scope.search = function(q) {
|
||||
var MIN_SEARCH_LENGTH = 2;
|
||||
if(q.length >= MIN_SEARCH_LENGTH) {
|
||||
var results = docsSearch(q);
|
||||
var totalAreas = 0;
|
||||
for(var i in results) {
|
||||
++totalAreas;
|
||||
}
|
||||
if(totalAreas > 0) {
|
||||
$scope.colClassName = 'cols-' + totalAreas;
|
||||
}
|
||||
$scope.hasResults = totalAreas > 0;
|
||||
$scope.results = results;
|
||||
docsSearch(q).then(function(hits) {
|
||||
var results = {};
|
||||
angular.forEach(hits, function(hit) {
|
||||
var area = hit.area;
|
||||
|
||||
var limit = (area == 'api') ? 40 : 14;
|
||||
results[area] = results[area] || [];
|
||||
if(results[area].length < limit) {
|
||||
results[area].push(hit);
|
||||
}
|
||||
});
|
||||
|
||||
var totalAreas = 0;
|
||||
for(var i in results) {
|
||||
++totalAreas;
|
||||
}
|
||||
if(totalAreas > 0) {
|
||||
$scope.colClassName = 'cols-' + totalAreas;
|
||||
}
|
||||
$scope.hasResults = totalAreas > 0;
|
||||
$scope.results = results;
|
||||
});
|
||||
}
|
||||
else {
|
||||
clearResults();
|
||||
}
|
||||
if(!$scope.$$phase) $scope.$apply();
|
||||
};
|
||||
|
||||
$scope.submit = function() {
|
||||
var result;
|
||||
for(var i in $scope.results) {
|
||||
@@ -39,78 +52,124 @@ angular.module('search', [])
|
||||
$scope.hideResults();
|
||||
}
|
||||
};
|
||||
|
||||
$scope.hideResults = function() {
|
||||
clearResults();
|
||||
$scope.q = '';
|
||||
};
|
||||
}])
|
||||
|
||||
.controller('Error404SearchCtrl', ['$scope', '$location', 'docsSearch', function($scope, $location, docsSearch) {
|
||||
$scope.results = docsSearch($location.path().split(/[\/\.:]/).pop());
|
||||
|
||||
.controller('Error404SearchCtrl', ['$scope', '$location', 'docsSearch',
|
||||
function($scope, $location, docsSearch) {
|
||||
docsSearch($location.path().split(/[\/\.:]/).pop()).then(function(results) {
|
||||
$scope.results = {};
|
||||
angular.forEach(results, function(result) {
|
||||
var area = $scope.results[result.area] || [];
|
||||
area.push(result);
|
||||
$scope.results[result.area] = area;
|
||||
});
|
||||
});
|
||||
}])
|
||||
|
||||
.factory('lunrSearch', function() {
|
||||
return function(properties) {
|
||||
if (window.RUNNING_IN_NG_TEST_RUNNER) return null;
|
||||
|
||||
var engine = lunr(properties);
|
||||
return {
|
||||
store : function(values) {
|
||||
engine.add(values);
|
||||
},
|
||||
search : function(q) {
|
||||
return engine.search(q);
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.provider('docsSearch', function() {
|
||||
|
||||
.factory('docsSearch', ['$rootScope','lunrSearch', 'NG_PAGES',
|
||||
function($rootScope, lunrSearch, NG_PAGES) {
|
||||
if (window.RUNNING_IN_NG_TEST_RUNNER) {
|
||||
return null;
|
||||
}
|
||||
// This version of the service builds the index in the current thread,
|
||||
// which blocks rendering and other browser activities.
|
||||
// It should only be used where the browser does not support WebWorkers
|
||||
function localSearchFactory($http, $timeout, NG_PAGES) {
|
||||
|
||||
var index = lunrSearch(function() {
|
||||
this.ref('id');
|
||||
this.field('title', {boost: 50});
|
||||
this.field('members', { boost: 40});
|
||||
this.field('keywords', { boost : 20 });
|
||||
});
|
||||
console.log('Using Local Search Index');
|
||||
|
||||
angular.forEach(NG_PAGES, function(page, key) {
|
||||
if(page.searchTerms) {
|
||||
index.store({
|
||||
id : key,
|
||||
title : page.searchTerms.titleWords,
|
||||
keywords : page.searchTerms.keywords,
|
||||
members : page.searchTerms.members
|
||||
// Create the lunr index
|
||||
var index = lunr(function() {
|
||||
this.ref('path');
|
||||
this.field('titleWords', {boost: 50});
|
||||
this.field('members', { boost: 40});
|
||||
this.field('keywords', { boost : 20 });
|
||||
});
|
||||
|
||||
// Delay building the index by loading the data asynchronously
|
||||
var indexReadyPromise = $http.get('js/search-data.json').then(function(response) {
|
||||
var searchData = response.data;
|
||||
// Delay building the index for 500ms to allow the page to render
|
||||
return $timeout(function() {
|
||||
// load the page data into the index
|
||||
angular.forEach(searchData, function(page) {
|
||||
index.add(page);
|
||||
});
|
||||
}, 500);
|
||||
});
|
||||
|
||||
// The actual service is a function that takes a query string and
|
||||
// returns a promise to the search results
|
||||
// (In this case we just resolve the promise immediately as it is not
|
||||
// inherently an async process)
|
||||
return function(q) {
|
||||
return indexReadyPromise.then(function() {
|
||||
var hits = index.search(q);
|
||||
var results = [];
|
||||
angular.forEach(hits, function(hit) {
|
||||
results.push(NG_PAGES[hit.ref]);
|
||||
});
|
||||
return results;
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
localSearchFactory.$inject = ['$http', '$timeout', 'NG_PAGES'];
|
||||
|
||||
return function(q) {
|
||||
var results = {
|
||||
api : [],
|
||||
tutorial : [],
|
||||
guide : [],
|
||||
error : [],
|
||||
misc : []
|
||||
// This version of the service builds the index in a WebWorker,
|
||||
// which does not block rendering and other browser activities.
|
||||
// It should only be used where the browser does support WebWorkers
|
||||
function webWorkerSearchFactory($q, $rootScope, NG_PAGES) {
|
||||
|
||||
console.log('Using WebWorker Search Index')
|
||||
|
||||
var searchIndex = $q.defer();
|
||||
var results;
|
||||
|
||||
var worker = new Worker('js/search-worker.js');
|
||||
|
||||
// The worker will send us a message in two situations:
|
||||
// - when the index has been built, ready to run a query
|
||||
// - when it has completed a search query and the results are available
|
||||
worker.onmessage = function(oEvent) {
|
||||
$rootScope.$apply(function() {
|
||||
|
||||
switch(oEvent.data.e) {
|
||||
case 'index-ready':
|
||||
searchIndex.resolve();
|
||||
break;
|
||||
case 'query-ready':
|
||||
var pages = oEvent.data.d.map(function(path) {
|
||||
return NG_PAGES[path];
|
||||
});
|
||||
results.resolve(pages);
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
angular.forEach(index.search(q), function(result) {
|
||||
var key = result.ref;
|
||||
var item = NG_PAGES[key];
|
||||
var area = item.area;
|
||||
item.path = key;
|
||||
|
||||
var limit = area == 'api' ? 40 : 14;
|
||||
if(results[area].length < limit) {
|
||||
results[area].push(item);
|
||||
}
|
||||
});
|
||||
return results;
|
||||
// The actual service is a function that takes a query string and
|
||||
// returns a promise to the search results
|
||||
return function(q) {
|
||||
|
||||
// We only run the query once the index is ready
|
||||
return searchIndex.promise.then(function() {
|
||||
|
||||
results = $q.defer();
|
||||
worker.postMessage({ q: q });
|
||||
return results.promise;
|
||||
});
|
||||
};
|
||||
}
|
||||
webWorkerSearchFactory.$inject = ['$q', '$rootScope', 'NG_PAGES'];
|
||||
|
||||
return {
|
||||
$get: window.Worker ? webWorkerSearchFactory : localSearchFactory
|
||||
};
|
||||
}])
|
||||
})
|
||||
|
||||
.directive('focused', function($timeout) {
|
||||
return function(scope, element, attrs) {
|
||||
|
||||
+15
-16
@@ -1,6 +1,6 @@
|
||||
angular.module('tutorials', [])
|
||||
|
||||
.directive('docTutorialNav', function(templateMerge) {
|
||||
.directive('docTutorialNav', function() {
|
||||
var pages = [
|
||||
'',
|
||||
'step_00', 'step_01', 'step_02', 'step_03', 'step_04',
|
||||
@@ -8,23 +8,22 @@ angular.module('tutorials', [])
|
||||
'step_10', 'step_11', 'step_12', 'the_end'
|
||||
];
|
||||
return {
|
||||
compile: function(element, attrs) {
|
||||
var seq = 1 * attrs.docTutorialNav,
|
||||
props = {
|
||||
seq: seq,
|
||||
prev: pages[seq],
|
||||
next: pages[2 + seq],
|
||||
diffLo: seq ? (seq - 1): '0~1',
|
||||
diffHi: seq
|
||||
};
|
||||
scope: {},
|
||||
template:
|
||||
'<a ng-href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-step-backward"></i> Previous</li></a>\n' +
|
||||
'<a ng-href="http://angular.github.io/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="glyphicon glyphicon-play"></i> Live Demo</li></a>\n' +
|
||||
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-search"></i> Code Diff</li></a>\n' +
|
||||
'<a ng-href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="glyphicon glyphicon-step-forward"></i></li></a>',
|
||||
link: function(scope, element, attrs) {
|
||||
var seq = 1 * attrs.docTutorialNav;
|
||||
scope.seq = seq;
|
||||
scope.prev = pages[seq];
|
||||
scope.next = pages[2 + seq];
|
||||
scope.diffLo = seq ? (seq - 1): '0~1';
|
||||
scope.diffHi = seq;
|
||||
|
||||
element.addClass('btn-group');
|
||||
element.addClass('tutorial-nav');
|
||||
element.append(templateMerge(
|
||||
'<a href="tutorial/{{prev}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-step-backward"></i> Previous</li></a>\n' +
|
||||
'<a href="http://angular.github.io/angular-phonecat/step-{{seq}}/app"><li class="btn btn-primary"><i class="glyphicon glyphicon-play"></i> Live Demo</li></a>\n' +
|
||||
'<a href="https://github.com/angular/angular-phonecat/compare/step-{{diffLo}}...step-{{diffHi}}"><li class="btn btn-primary"><i class="glyphicon glyphicon-search"></i> Code Diff</li></a>\n' +
|
||||
'<a href="tutorial/{{next}}"><li class="btn btn-primary">Next <i class="glyphicon glyphicon-step-forward"></i></li></a>', props));
|
||||
}
|
||||
};
|
||||
})
|
||||
@@ -47,4 +46,4 @@ angular.module('tutorials', [])
|
||||
'<a ng-href="https://github.com/angular/angular-phonecat/compare/step-{{step ? (step - 1): \'0~1\'}}...step-{{step}}">GitHub</a>\n' +
|
||||
'</p>'
|
||||
};
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,3 @@
|
||||
"use strict";
|
||||
|
||||
angular.module('versions', [])
|
||||
|
||||
.controller('DocsVersionsCtrl', ['$scope', '$location', '$window', 'NG_VERSIONS', function($scope, $location, $window, NG_VERSIONS) {
|
||||
|
||||
@@ -19,7 +19,7 @@ describe("DocsController", function() {
|
||||
it("should update the Google Analytics with currentPage path if currentPage exists", inject(function($window) {
|
||||
$window._gaq = [];
|
||||
$scope.currentPage = { path: 'a/b/c' };
|
||||
$scope.afterPartialLoaded();
|
||||
$scope.$broadcast('$includeContentLoaded');
|
||||
expect($window._gaq.pop()).toEqual(['_trackPageview', 'a/b/c']);
|
||||
}));
|
||||
|
||||
@@ -27,7 +27,7 @@ describe("DocsController", function() {
|
||||
it("should update the Google Analytics with $location.path if currentPage is missing", inject(function($window, $location) {
|
||||
$window._gaq = [];
|
||||
spyOn($location, 'path').andReturn('x/y/z');
|
||||
$scope.afterPartialLoaded();
|
||||
$scope.$broadcast('$includeContentLoaded');
|
||||
expect($window._gaq.pop()).toEqual(['_trackPageview', 'x/y/z']);
|
||||
}));
|
||||
});
|
||||
|
||||
+11
-4
@@ -92,10 +92,7 @@ module.exports = new Package('angularjs', [
|
||||
}
|
||||
return docPath;
|
||||
},
|
||||
getOutputPath: function(doc) {
|
||||
return 'partials/' + doc.path +
|
||||
( doc.fileInfo.baseName === 'index' ? '/index.html' : '.html');
|
||||
}
|
||||
outputPathTemplate: 'partials/${path}.html'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
@@ -110,6 +107,16 @@ module.exports = new Package('angularjs', [
|
||||
outputPathTemplate: '${id}.html'
|
||||
});
|
||||
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['module' ],
|
||||
pathTemplate: '${area}/${name}',
|
||||
outputPathTemplate: 'partials/${area}/${name}.html'
|
||||
});
|
||||
computePathsProcessor.pathTemplates.push({
|
||||
docTypes: ['componentGroup' ],
|
||||
pathTemplate: '${area}/${moduleName}/${groupType}',
|
||||
outputPathTemplate: 'partials/${area}/${moduleName}/${groupType}.html'
|
||||
});
|
||||
|
||||
computeIdsProcessor.idTemplates.push({
|
||||
docTypes: ['overview', 'tutorial', 'e2e-test', 'indexPage'],
|
||||
|
||||
@@ -147,24 +147,18 @@ module.exports = function generatePagesDataProcessor(log) {
|
||||
};
|
||||
|
||||
return {
|
||||
$runAfter: ['paths-computed'],
|
||||
$runAfter: ['paths-computed', 'generateKeywordsProcessor'],
|
||||
$runBefore: ['rendering-docs'],
|
||||
$process: function(docs) {
|
||||
|
||||
_(docs)
|
||||
.filter(function(doc) { return doc.area === 'api' && doc.docType === 'module'; })
|
||||
.forEach(function(doc) { if ( !doc.path ) {
|
||||
log.warn('Missing path property for ', doc.id);
|
||||
}})
|
||||
.map(function(doc) { return _.pick(doc, ['id', 'module', 'docType', 'area']); })
|
||||
.tap(function(docs) {
|
||||
log.debug(docs);
|
||||
// We are only interested in docs that are in an area
|
||||
var pages = _.filter(docs, function(doc) {
|
||||
return doc.area;
|
||||
});
|
||||
|
||||
|
||||
// We are only interested in docs that are in an area and are not landing pages
|
||||
var navPages = _.filter(docs, function(page) {
|
||||
return page.area && page.docType != 'componentGroup';
|
||||
// We are only interested in pages that are not landing pages
|
||||
var navPages = _.filter(pages, function(page) {
|
||||
return page.docType != 'componentGroup';
|
||||
});
|
||||
|
||||
// Generate an object collection of pages that is grouped by area e.g.
|
||||
@@ -198,28 +192,48 @@ module.exports = function generatePagesDataProcessor(log) {
|
||||
area.navGroups = navGroupMapper(pages, area);
|
||||
});
|
||||
|
||||
docs.push({
|
||||
docType: 'nav-data',
|
||||
id: 'nav-data',
|
||||
template: 'nav-data.template.js',
|
||||
outputPath: 'js/nav-data.js',
|
||||
areas: areas
|
||||
});
|
||||
|
||||
|
||||
|
||||
var searchData = _(pages)
|
||||
.filter(function(page) {
|
||||
return page.searchTerms;
|
||||
})
|
||||
.map(function(page) {
|
||||
return _.extend({ path: page.path }, page.searchTerms);
|
||||
})
|
||||
.value();
|
||||
|
||||
docs.push({
|
||||
docType: 'json-doc',
|
||||
id: 'search-data-json',
|
||||
template: 'json-doc.template.json',
|
||||
outputPath: 'js/search-data.json',
|
||||
data: searchData
|
||||
});
|
||||
|
||||
// Extract a list of basic page information for mapping paths to partials and for client side searching
|
||||
var pages = _(docs)
|
||||
var pageData = _(docs)
|
||||
.map(function(doc) {
|
||||
var page = _.pick(doc, [
|
||||
'docType', 'id', 'name', 'area', 'outputPath', 'path', 'searchTerms'
|
||||
]);
|
||||
return page;
|
||||
return _.pick(doc, ['name', 'area', 'path']);
|
||||
})
|
||||
.indexBy('path')
|
||||
.value();
|
||||
|
||||
var docData = {
|
||||
docs.push({
|
||||
docType: 'pages-data',
|
||||
id: 'pages-data',
|
||||
template: 'pages-data.template.js',
|
||||
outputPath: 'js/pages-data.js',
|
||||
|
||||
areas: areas,
|
||||
pages: pages
|
||||
};
|
||||
|
||||
docs.push(docData);
|
||||
pages: pageData
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -19,13 +19,13 @@ module.exports = function debugDeployment(getVersion) {
|
||||
'../angular-animate.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.js',
|
||||
'js/angular-bootstrap/bootstrap-prettify.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/nav-data.js',
|
||||
'js/docs.js'
|
||||
],
|
||||
stylesheets: [
|
||||
|
||||
@@ -18,15 +18,15 @@ module.exports = function defaultDeployment(getVersion) {
|
||||
'../angular-touch.min.js',
|
||||
'../angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.js',
|
||||
'js/angular-bootstrap/bootstrap-prettify.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.js',
|
||||
'js/angular-bootstrap/bootstrap.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/docs.js'
|
||||
'js/nav-data.js',
|
||||
'js/docs.min.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
|
||||
+4
-4
@@ -22,15 +22,15 @@ module.exports = function jqueryDeployment(getVersion) {
|
||||
'../angular-touch.min.js',
|
||||
'../angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.js',
|
||||
'js/angular-bootstrap/bootstrap-prettify.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.js',
|
||||
'js/angular-bootstrap/bootstrap.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/docs.js'
|
||||
'js/nav-data.js',
|
||||
'js/docs.min.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
|
||||
@@ -21,15 +21,15 @@ module.exports = function productionDeployment(getVersion) {
|
||||
cdnUrl + '/angular-touch.min.js',
|
||||
cdnUrl + '/angular-animate.min.js',
|
||||
'components/marked-' + getVersion('marked', 'node_modules', 'package.json') + '/lib/marked.js',
|
||||
'js/angular-bootstrap/bootstrap.js',
|
||||
'js/angular-bootstrap/bootstrap-prettify.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.js',
|
||||
'js/angular-bootstrap/bootstrap.min.js',
|
||||
'js/angular-bootstrap/dropdown-toggle.min.js',
|
||||
'components/lunr.js-' + getVersion('lunr.js') + '/lunr.min.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/prettify.js',
|
||||
'components/google-code-prettify-' + getVersion('google-code-prettify') + '/src/lang-css.js',
|
||||
'js/versions-data.js',
|
||||
'js/pages-data.js',
|
||||
'js/docs.js'
|
||||
'js/nav-data.js',
|
||||
'js/docs.min.js'
|
||||
],
|
||||
stylesheets: [
|
||||
'components/bootstrap-' + getVersion('bootstrap') + '/css/bootstrap.min.css',
|
||||
|
||||
@@ -56,15 +56,6 @@
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
// force page reload when new update is available
|
||||
window.applicationCache && window.applicationCache.addEventListener('updateready', function(e) {
|
||||
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
|
||||
window.applicationCache.swapCache();
|
||||
window.location.reload();
|
||||
}
|
||||
}, false);
|
||||
|
||||
// GA asynchronous tracker
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-8594346-3']);
|
||||
@@ -85,7 +76,7 @@
|
||||
<div class="row">
|
||||
<div class="col-md-9 header-branding">
|
||||
<a class="brand navbar-brand" href="http://angularjs.org">
|
||||
<img class="logo" src="img/angularjs-for-header-only.svg">
|
||||
<img width="117" height="30" class="logo" ng-src="img/angularjs-for-header-only.svg">
|
||||
</a>
|
||||
<ul class="nav navbar-nav">
|
||||
<li class="divider-vertical"></li>
|
||||
@@ -219,7 +210,7 @@
|
||||
</div>
|
||||
<div class="grid-right">
|
||||
<div id="loading" ng-show="loading">Loading...</div>
|
||||
<div ng-hide="loading" ng-include="currentPage.outputPath || 'Error404.html'" onload="afterPartialLoaded()" autoscroll></div>
|
||||
<div ng-hide="loading" ng-include="partialPath" autoscroll></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{$ doc.data | json $}
|
||||
@@ -0,0 +1,3 @@
|
||||
// Meta data used by the AngularJS docs app
|
||||
angular.module('navData', [])
|
||||
.value('NG_NAVIGATION', {$ doc.areas | json $});
|
||||
@@ -1,4 +1,3 @@
|
||||
// Meta data used by the AngularJS docs app
|
||||
angular.module('pagesData', [])
|
||||
.value('NG_PAGES', {$ doc.pages | json $})
|
||||
.value('NG_NAVIGATION', {$ doc.areas | json $});
|
||||
.value('NG_PAGES', {$ doc.pages | json $});
|
||||
|
||||
@@ -12,10 +12,10 @@ $controller(MyController);
|
||||
$controller(MyController, {scope: newScope});
|
||||
```
|
||||
|
||||
To fix the example above please provide a scope to the $controller call:
|
||||
To fix the example above please provide a scope (using the `$scope` property in the locals object) to the $controller call:
|
||||
|
||||
```
|
||||
$controller(MyController, {$scope, newScope});
|
||||
$controller(MyController, {$scope: newScope});
|
||||
```
|
||||
|
||||
Please consult the {@link ng.$controller $controller} service api docs to learn more.
|
||||
|
||||
@@ -54,4 +54,18 @@ angular.module('myModule')
|
||||
.directive('myDirective', ['myCoolService', function (myCoolService) {
|
||||
// This directive definition does not throw unknown provider.
|
||||
}]);
|
||||
```
|
||||
```
|
||||
|
||||
|
||||
Attempting to inject one controller into another will also throw an `Unknown provider` error:
|
||||
|
||||
```
|
||||
angular.module('myModule', [])
|
||||
.controller('MyFirstController', function() { /* ... */ });
|
||||
.controller('MySecondController', ['MyFirstController', function(MyFirstController) {
|
||||
// This controller throws an unknown provider error because
|
||||
// MyFirstController cannot be injected.
|
||||
}]);
|
||||
```
|
||||
|
||||
Use the `$controller` service if you want to instantiate controllers yourself.
|
||||
|
||||
@@ -164,7 +164,7 @@ encoded.
|
||||
|
||||
`$location` service has two configuration modes which control the format of the URL in the browser
|
||||
address bar: **Hashbang mode** (the default) and the **HTML5 mode** which is based on using the
|
||||
HTML5 [History API](http://www.w3.org/TR/html5/history.html). Applications use the same API in
|
||||
HTML5 [History API](http://www.w3.org/TR/html5/introduction.html#history-0). Applications use the same API in
|
||||
both modes and the `$location` service will work with appropriate URL segments and browser APIs to
|
||||
facilitate the browser URL change and history management.
|
||||
|
||||
@@ -241,7 +241,7 @@ it('should show example', inject(
|
||||
## HTML5 mode
|
||||
|
||||
In HTML5 mode, the `$location` service getters and setters interact with the browser URL address
|
||||
through the HTML5 history API, which allows for use of regular URL path and search segments,
|
||||
through the HTML5 history API. This allows for use of regular URL path and search segments,
|
||||
instead of their hashbang equivalents. If the HTML5 History API is not supported by a browser, the
|
||||
`$location` service will fall back to using the hashbang URLs automatically. This frees you from
|
||||
having to worry about whether the browser displaying your app supports the history API or not; the
|
||||
|
||||
@@ -91,7 +91,9 @@ Here is an example of manually initializing Angular:
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body>
|
||||
Hello {{greetMe}}!
|
||||
<div ng-controller="MyController">
|
||||
Hello {{greetMe}}!
|
||||
</div>
|
||||
<script src="http://code.angularjs.org/snapshot/angular.js"></script>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -85,7 +85,9 @@ Here is a directive which makes any element draggable. Notice the `draggable` at
|
||||
position: 'relative',
|
||||
border: '1px solid red',
|
||||
backgroundColor: 'lightgrey',
|
||||
cursor: 'pointer'
|
||||
cursor: 'pointer',
|
||||
display: 'block',
|
||||
width: '65px'
|
||||
});
|
||||
element.on('mousedown', function(event) {
|
||||
// Prevent default dragging of selected content
|
||||
|
||||
@@ -67,8 +67,8 @@ element that adds extra behavior to the element. The {@link ng.directive:ngModel
|
||||
stores/updates the value of the input field into/from a variable.
|
||||
|
||||
<div class="alert alert-info">
|
||||
**Custom directives to access the DOM**: In Angular, the only place where an application touches the DOM is
|
||||
within directives. This is good as artifacts that access the DOM are hard to test.
|
||||
**Custom directives to access the DOM**: In Angular, the only place where an application should access the DOM is
|
||||
within directives. This is important because artifacts that access the DOM are hard to test.
|
||||
If you need to access the DOM directly you should write a custom directive for this. The
|
||||
{@link directive directives guide} explains how to do this.
|
||||
</div>
|
||||
|
||||
@@ -160,7 +160,7 @@ restrictions, you cannot simply write `cx="{{cx}}"`.
|
||||
With `ng-attr-cx` you can work around this problem.
|
||||
|
||||
If an attribute with a binding is prefixed with the `ngAttr` prefix (denormalized as `ng-attr-`)
|
||||
then during the binding will be applied to the corresponding unprefixed attribute. This allows
|
||||
then during the binding it will be applied to the corresponding unprefixed attribute. This allows
|
||||
you to bind to attributes that would otherwise be eagerly processed by browsers
|
||||
(e.g. an SVG element's `circle[cx]` attributes).
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ In Angular applications, you move the job of filling page templates with data fr
|
||||
|
||||
This is a short list of libraries with specific support and documentation for working with Angular. You can find a full list of all known Angular external libraries at [ngmodules.org](http://ngmodules.org/).
|
||||
|
||||
* **Internationalization:** [angular-translate](http://angular-translate.github.io), [angular-gettext](http://angular-gettext.rocketeer.be/)
|
||||
* **Internationalization:** [angular-translate](http://angular-translate.github.io), [angular-gettext](http://angular-gettext.rocketeer.be/), [angular-localization](http://doshprompt.github.io/angular-localization/)
|
||||
* **RESTful services:** [Restangular](https://github.com/mgonto/restangular)
|
||||
* **SQL and NoSQL backends:** [BreezeJS](http://www.breezejs.com/), [AngularFire](http://angularfire.com/)
|
||||
* **UI Widgets: **[KendoUI](http://kendo-labs.github.io/angular-kendo/#/), [UI Bootstrap](http://angular-ui.github.io/bootstrap/), [Wijmo](http://wijmo.com/tag/angularjs-2/), [ngTagsInput](https://github.com/mbenford/ngTagsInput)
|
||||
@@ -104,6 +104,7 @@ This is a short list of libraries with specific support and documentation for wo
|
||||
* [Recipes With AngularJS](http://www.amazon.co.uk/Recipes-Angular-js-Frederik-Dietz-ebook/dp/B00DK95V48) by Frederik Dietz
|
||||
* [Developing an AngularJS Edge](http://www.amazon.com/Developing-AngularJS-Edge-Christopher-Hiller-ebook/dp/B00CJLFF8K) by Christopher Hiller
|
||||
* [ng-book: The Complete Book on AngularJS](http://ng-book.com/) by Ari Lerner
|
||||
* [AngularJS : Novice to Ninja](http://www.amazon.in/AngularJS-Novice-Ninja-Sandeep-Panda/dp/0992279453) by Sandeep Panda
|
||||
|
||||
###Videos:
|
||||
* [egghead.io](http://egghead.io/)
|
||||
|
||||
@@ -204,7 +204,7 @@ If you want to apply a directive to each inner piece of the repeat, put it on a
|
||||
|
||||
### `$rootScope` exists, but it can be used for evil
|
||||
|
||||
Scopes in Angular form a hierarchy, prototypically inheriting from a root scope at the top of the tree.
|
||||
Scopes in Angular form a hierarchy, prototypally inheriting from a root scope at the top of the tree.
|
||||
Usually this can be ignored, since most views have a controller, and therefore a scope, of their own.
|
||||
|
||||
Occasionally there are pieces of data that you want to make global to the whole app.
|
||||
|
||||
@@ -233,7 +233,7 @@ browser is limited, which results in your karma tests running extremely slow.
|
||||
|
||||
Refresh your browser and verify that it says "Hello, World!".
|
||||
|
||||
* Update the unit test for the controller in ./test/unit/controllersSpec.js to reflect the previous change. For example by adding:
|
||||
* Update the unit test for the controller in `./test/unit/controllersSpec.js` to reflect the previous change. For example by adding:
|
||||
|
||||
expect(scope.name).toBe('World');
|
||||
|
||||
@@ -251,7 +251,7 @@ browser is limited, which results in your karma tests running extremely slow.
|
||||
<tr ng-repeat="i in [0, 1, 2, 3, 4, 5, 6, 7]"><td>{{i+1}}</td></tr>
|
||||
</table>
|
||||
|
||||
Extra points: try and make an 8x8 table using an additional ng-repeat.
|
||||
Extra points: try and make an 8x8 table using an additional `ng-repeat`.
|
||||
|
||||
* Make the unit test fail by changing `expect(scope.phones.length).toBe(3)` to instead use `toBe(4)`.
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ describe('PhoneCat App', function() {
|
||||
});
|
||||
|
||||
|
||||
it('should filter the phone list as user types into the search box', function() {
|
||||
it('should filter the phone list as a user types into the search box', function() {
|
||||
|
||||
var phoneList = element.all(by.repeater('phone in phones'));
|
||||
var query = element(by.model('query'));
|
||||
@@ -159,7 +159,7 @@ Let's see how we can get the current value of the `query` model to appear in the
|
||||
var phoneList = element.all(by.repeater('phone in phones'));
|
||||
var query = element(by.model('query'));
|
||||
|
||||
it('should filter the phone list as user types into the search box', function() {
|
||||
it('should filter the phone list as a user types into the search box', function() {
|
||||
expect(phoneList.count()).toBe(3);
|
||||
|
||||
query.sendKeys('nexus');
|
||||
|
||||
@@ -11,7 +11,7 @@ from our server using one of Angular's built-in {@link guide/services services}
|
||||
ng.$http $http}. We will use Angular's {@link guide/di dependency
|
||||
injection (DI)} to provide the service to the `PhoneListCtrl` controller.
|
||||
|
||||
* There are now a list of 20 phones, loaded from the server.
|
||||
* There is now a list of 20 phones, loaded from the server.
|
||||
|
||||
<div doc-tutorial-reset="5"></div>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ animations on top of the template code we created before.
|
||||
## Dependencies
|
||||
|
||||
The animation functionality is provided by Angular in the `ngAnimate` module, which is distributed
|
||||
separately from the core Angular framework. In addition we will use `JQuery` in this project to do
|
||||
separately from the core Angular framework. In addition we will use `jQuery` in this project to do
|
||||
extra JavaScript animations.
|
||||
|
||||
We are using [Bower][bower] to install client side dependencies. This step updates the
|
||||
@@ -49,8 +49,8 @@ We are using [Bower][bower] to install client side dependencies. This step upda
|
||||
|
||||
* `"angular-animate": "~1.2.x"` tells bower to install a version of the
|
||||
angular-animate component that is compatible with version 1.2.x.
|
||||
* `"jquery": "1.10.2"` tells bower to install the 1.10.2 version of JQuery. Note that this is not an
|
||||
Angular library, it is the standard JQuery library. We can use bower to install a wide range of 3rd
|
||||
* `"jquery": "1.10.2"` tells bower to install the 1.10.2 version of jQuery. Note that this is not an
|
||||
Angular library, it is the standard jQuery library. We can use bower to install a wide range of 3rd
|
||||
party libraries.
|
||||
|
||||
We must ask bower to download and install this dependency. We can do this by running:
|
||||
@@ -254,7 +254,7 @@ which are described in detail below.
|
||||
Next let's add an animation for transitions between route changes in {@link api/ngRoute.directive:ngView `ngView`}.
|
||||
|
||||
To start, let's add a new CSS class to our HTML like we did in the example above.
|
||||
This time, instead of the `ng-repeat` element, let's add it to the element containing the ng-view directive.
|
||||
This time, instead of the `ng-repeat` element, let's add it to the element containing the `ng-view` directive.
|
||||
In order to do this, we'll have to make some small changes to the HTML code so that we can have more control over our
|
||||
animations between view changes.
|
||||
|
||||
@@ -339,13 +339,13 @@ a cross fade animation in between. So as the previous page is just about to be r
|
||||
while the new page fades in right on top of it.
|
||||
|
||||
Once the leave animation is over then element is removed and once the enter animation is complete
|
||||
then the `ng-enter` and `ng-enter-active` CSS classes are removed from the element rendering it to
|
||||
be position itself with its default CSS code (so no more absolute positioning once the animation is
|
||||
then the `ng-enter` and `ng-enter-active` CSS classes are removed from the element, causing it to rerender and
|
||||
reposition itself with its default CSS code (so no more absolute positioning once the animation is
|
||||
over). This works fluidly so that pages flow naturally between route changes without anything
|
||||
jumping around.
|
||||
|
||||
The CSS classes applied (the start and end classes) are much the same as with `ng-repeat`. Each time
|
||||
a new page is loaded the ng-view directive will create a copy of itself, download the template and
|
||||
a new page is loaded the `ng-view` directive will create a copy of itself, download the template and
|
||||
append the contents. This ensures that all views are contained within a single HTML element which
|
||||
allows for easy animation control.
|
||||
|
||||
|
||||
+37
-5
@@ -8,7 +8,10 @@ var bower = require('bower');
|
||||
var Dgeni = require('dgeni');
|
||||
var merge = require('event-stream').merge;
|
||||
var path = require('canonical-path');
|
||||
|
||||
var foreach = require('gulp-foreach');
|
||||
var uglify = require('gulp-uglify');
|
||||
var sourcemaps = require('gulp-sourcemaps');
|
||||
var rename = require('gulp-rename');
|
||||
|
||||
// We indicate to gulp that tasks are async by returning the stream.
|
||||
// Gulp can then wait for the stream to close before starting dependent tasks.
|
||||
@@ -17,6 +20,9 @@ var path = require('canonical-path');
|
||||
var outputFolder = '../build/docs';
|
||||
var bowerFolder = 'bower_components';
|
||||
|
||||
var src = 'app/src/**/*.js';
|
||||
var assets = 'app/assets/**/*';
|
||||
|
||||
|
||||
var copyComponent = function(component, pattern, sourceFolder, packageFile) {
|
||||
pattern = pattern || '/**/*';
|
||||
@@ -40,14 +46,37 @@ gulp.task('bower', function() {
|
||||
});
|
||||
|
||||
gulp.task('build-app', function() {
|
||||
gulp.src('app/src/**/*.js')
|
||||
.pipe(concat('docs.js'))
|
||||
.pipe(gulp.dest(outputFolder + '/js/'));
|
||||
var file = 'docs.js';
|
||||
var minFile = 'docs.min.js';
|
||||
var folder = outputFolder + '/js/';
|
||||
|
||||
return gulp.src(src)
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(concat(file))
|
||||
.pipe(gulp.dest(folder))
|
||||
.pipe(rename(minFile))
|
||||
.pipe(uglify())
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest(folder));
|
||||
});
|
||||
|
||||
gulp.task('assets', ['bower'], function() {
|
||||
var JS_EXT = /\.js$/;
|
||||
return merge(
|
||||
gulp.src(['app/assets/**/*']).pipe(gulp.dest(outputFolder)),
|
||||
gulp.src([assets])
|
||||
.pipe(gulp.dest(outputFolder)),
|
||||
gulp.src([assets])
|
||||
.pipe(foreach(function(stream, file) {
|
||||
if (JS_EXT.test(file.relative)) {
|
||||
var minFile = file.relative.replace(JS_EXT, '.min.js');
|
||||
return stream
|
||||
.pipe(sourcemaps.init())
|
||||
.pipe(concat(minFile))
|
||||
.pipe(uglify())
|
||||
.pipe(sourcemaps.write('.'))
|
||||
.pipe(gulp.dest(outputFolder));
|
||||
}
|
||||
})),
|
||||
copyComponent('bootstrap', '/dist/**/*'),
|
||||
copyComponent('open-sans-fontface'),
|
||||
copyComponent('lunr.js','/*.js'),
|
||||
@@ -77,3 +106,6 @@ gulp.task('jshint', ['doc-gen'], function() {
|
||||
// The default task that will be run if no task is supplied
|
||||
gulp.task('default', ['assets', 'doc-gen', 'build-app', 'jshint']);
|
||||
|
||||
gulp.task('watch', function() {
|
||||
gulp.watch([src, assets], ['assets', 'build-app']);
|
||||
});
|
||||
|
||||
@@ -83,6 +83,7 @@ var getTaggedVersion = function() {
|
||||
if ( version && semver.satisfies(version, currentPackage.branchVersion)) {
|
||||
version.codeName = getCodeName(tag);
|
||||
version.full = version.version;
|
||||
version.branch = 'v' + currentPackage.branchVersion.replace('*', 'x');
|
||||
return version;
|
||||
}
|
||||
}
|
||||
@@ -197,6 +198,7 @@ var getSnapshotVersion = function() {
|
||||
version.isSnapshot = true;
|
||||
version.format();
|
||||
version.full = version.version + '+' + version.build;
|
||||
version.branch = 'master';
|
||||
|
||||
return version;
|
||||
};
|
||||
|
||||
Generated
+796
-342
File diff suppressed because it is too large
Load Diff
+7
-6
@@ -13,7 +13,6 @@
|
||||
"canonical-path": "0.0.2",
|
||||
"dgeni": "^0.4.0",
|
||||
"dgeni-packages": "^0.10.0",
|
||||
"es6-shim": "^0.14.0",
|
||||
"event-stream": "~3.1.0",
|
||||
"grunt": "~0.4.2",
|
||||
"grunt-bump": "~0.0.13",
|
||||
@@ -29,8 +28,12 @@
|
||||
"grunt-parallel": "~0.3.1",
|
||||
"grunt-shell": "~0.4.0",
|
||||
"gulp": "~3.8.0",
|
||||
"gulp-concat": "~2.1.7",
|
||||
"gulp-concat": "^2.4.1",
|
||||
"gulp-foreach": "0.0.1",
|
||||
"gulp-jshint": "~1.4.2",
|
||||
"gulp-rename": "^1.2.0",
|
||||
"gulp-sourcemaps": "^1.2.2",
|
||||
"gulp-uglify": "^1.0.1",
|
||||
"gulp-util": "^3.0.1",
|
||||
"jasmine-node": "~1.11.0",
|
||||
"jasmine-reporters": "~0.2.1",
|
||||
@@ -49,7 +52,7 @@
|
||||
"marked": "~0.3.0",
|
||||
"node-html-encoder": "0.0.2",
|
||||
"promises-aplus-tests": "~1.3.2",
|
||||
"protractor": "1.2.0",
|
||||
"protractor": "1.3.1",
|
||||
"q": "~1.0.0",
|
||||
"q-io": "^1.10.9",
|
||||
"qq": "^0.3.5",
|
||||
@@ -57,9 +60,7 @@
|
||||
"semver": "~2.1.0",
|
||||
"shelljs": "~0.2.6",
|
||||
"sorted-object": "^1.0.0",
|
||||
"stringmap": "^0.2.2",
|
||||
"winston": "~0.7.2",
|
||||
"yaml-js": "~0.0.8"
|
||||
"stringmap": "^0.2.2"
|
||||
},
|
||||
"licenses": [
|
||||
{
|
||||
|
||||
@@ -21,9 +21,9 @@ exports.config = {
|
||||
|
||||
// Disable animations so e2e tests run more quickly
|
||||
var disableNgAnimate = function() {
|
||||
angular.module('disableNgAnimate', []).run(function($animate) {
|
||||
angular.module('disableNgAnimate', []).run(['$animate', function($animate) {
|
||||
$animate.enabled(false);
|
||||
});
|
||||
}]);
|
||||
};
|
||||
|
||||
browser.addMockModule('disableNgAnimate', disableNgAnimate);
|
||||
|
||||
@@ -12,9 +12,9 @@ exports.config = {
|
||||
|
||||
// Disable animations so e2e tests run more quickly
|
||||
var disableNgAnimate = function() {
|
||||
angular.module('disableNgAnimate', []).run(function($animate) {
|
||||
angular.module('disableNgAnimate', []).run(['$animate', function($animate) {
|
||||
$animate.enabled(false);
|
||||
});
|
||||
}]);
|
||||
};
|
||||
|
||||
browser.addMockModule('disableNgAnimate', disableNgAnimate);
|
||||
|
||||
+1
-1
@@ -338,7 +338,7 @@ function setHashKey(obj, h) {
|
||||
* @kind function
|
||||
*
|
||||
* @description
|
||||
* Extends the destination object `dst` by copying all of the properties from the `src` object(s)
|
||||
* Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
|
||||
* to `dst`. You can specify multiple `src` objects.
|
||||
*
|
||||
* @param {Object} dst Destination object.
|
||||
|
||||
+1
-1
@@ -392,7 +392,7 @@
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Below is an example using `$compileProvider`.
|
||||
* ## Example
|
||||
*
|
||||
* <div class="alert alert-warning">
|
||||
* **Note**: Typically directives are registered with `module.directive`. The example below is
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
*
|
||||
* @element ANY
|
||||
* @scope
|
||||
* @priority 500
|
||||
* @param {expression} ngController Name of a globally accessible constructor function or an
|
||||
* {@link guide/expression expression} that on the current scope evaluates to a
|
||||
* constructor function. The controller instance can be published into a scope property
|
||||
|
||||
@@ -589,6 +589,7 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
id: option.id,
|
||||
selected: option.selected
|
||||
});
|
||||
selectCtrl.addOption(option.label, element);
|
||||
if (lastElement) {
|
||||
lastElement.after(element);
|
||||
} else {
|
||||
@@ -600,7 +601,9 @@ var selectDirective = ['$compile', '$parse', function($compile, $parse) {
|
||||
// remove any excessive OPTIONs in a group
|
||||
index++; // increment since the existingOptions[0] is parent element not OPTION
|
||||
while(existingOptions.length > index) {
|
||||
existingOptions.pop().element.remove();
|
||||
option = existingOptions.pop();
|
||||
selectCtrl.removeOption(option.label);
|
||||
option.element.remove();
|
||||
}
|
||||
}
|
||||
// remove any excessive OPTGROUPs from select
|
||||
|
||||
+11
-10
@@ -31,9 +31,9 @@
|
||||
}]);
|
||||
</script>
|
||||
<div ng-controller="ExampleController">
|
||||
Limit {{numbers}} to: <input type="integer" ng-model="numLimit">
|
||||
Limit {{numbers}} to: <input type="number" step="1" ng-model="numLimit">
|
||||
<p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
|
||||
Limit {{letters}} to: <input type="integer" ng-model="letterLimit">
|
||||
Limit {{letters}} to: <input type="number" step="1" ng-model="letterLimit">
|
||||
<p>Output letters: {{ letters | limitTo:letterLimit }}</p>
|
||||
</div>
|
||||
</file>
|
||||
@@ -50,14 +50,15 @@
|
||||
expect(limitedLetters.getText()).toEqual('Output letters: abc');
|
||||
});
|
||||
|
||||
it('should update the output when -3 is entered', function() {
|
||||
numLimitInput.clear();
|
||||
numLimitInput.sendKeys('-3');
|
||||
letterLimitInput.clear();
|
||||
letterLimitInput.sendKeys('-3');
|
||||
expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
|
||||
expect(limitedLetters.getText()).toEqual('Output letters: ghi');
|
||||
});
|
||||
// There is a bug in safari and protractor that doesn't like the minus key
|
||||
// it('should update the output when -3 is entered', function() {
|
||||
// numLimitInput.clear();
|
||||
// numLimitInput.sendKeys('-3');
|
||||
// letterLimitInput.clear();
|
||||
// letterLimitInput.sendKeys('-3');
|
||||
// expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
|
||||
// expect(limitedLetters.getText()).toEqual('Output letters: ghi');
|
||||
// });
|
||||
|
||||
it('should not exceed the maximum size of input array', function() {
|
||||
numLimitInput.clear();
|
||||
|
||||
+7
-6
@@ -671,12 +671,13 @@ function $HttpProvider() {
|
||||
expect(data.getText()).toMatch(/Hello, \$http!/);
|
||||
});
|
||||
|
||||
it('should make a JSONP request to angularjs.org', function() {
|
||||
sampleJsonpBtn.click();
|
||||
fetchBtn.click();
|
||||
expect(status.getText()).toMatch('200');
|
||||
expect(data.getText()).toMatch(/Super Hero!/);
|
||||
});
|
||||
// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
|
||||
// it('should make a JSONP request to angularjs.org', function() {
|
||||
// sampleJsonpBtn.click();
|
||||
// fetchBtn.click();
|
||||
// expect(status.getText()).toMatch('200');
|
||||
// expect(data.getText()).toMatch(/Super Hero!/);
|
||||
// });
|
||||
|
||||
it('should make JSONP request to invalid URL and invoke the error handler',
|
||||
function() {
|
||||
|
||||
@@ -265,9 +265,6 @@ function $RouteProvider(){
|
||||
* 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">
|
||||
|
||||
+2
-1
@@ -162,6 +162,7 @@
|
||||
"provideLog": false,
|
||||
"spyOnlyCallsWithArgs": false,
|
||||
"createMockStyleSheet": false,
|
||||
"browserTrigger": false
|
||||
"browserTrigger": false,
|
||||
"jqLiteCacheSize": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,15 @@ function dealoc(obj) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function jqLiteCacheSize() {
|
||||
var size = 0;
|
||||
forEach(jqLite.cache, function() { size++; });
|
||||
return size - jqLiteCacheSize.initSize;
|
||||
}
|
||||
jqLiteCacheSize.initSize = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @param {DOMElement} element
|
||||
* @param {boolean=} showNgClass
|
||||
|
||||
@@ -378,6 +378,39 @@ describe('select', function() {
|
||||
expect(element).toEqualSelect(['? string:r2d2 ?']);
|
||||
expect(scope.robot).toBe('r2d2');
|
||||
});
|
||||
|
||||
describe('selectController.hasOption', function() {
|
||||
it('should return true for options added via ngOptions', function() {
|
||||
scope.robots = [
|
||||
{key: 1, value: 'c3p0'},
|
||||
{key: 2, value: 'r2d2'}
|
||||
];
|
||||
scope.robot = 'r2d2';
|
||||
|
||||
compile('<select ng-model="robot" ' +
|
||||
'ng-options="item.key as item.value for item in robots">' +
|
||||
'</select>');
|
||||
|
||||
var selectCtrl = element.data().$selectController;
|
||||
|
||||
expect(selectCtrl.hasOption('c3p0')).toBe(true);
|
||||
expect(selectCtrl.hasOption('r2d2')).toBe(true);
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.robots.pop();
|
||||
});
|
||||
|
||||
expect(selectCtrl.hasOption('c3p0')).toBe(true);
|
||||
expect(selectCtrl.hasOption('r2d2')).toBe(false);
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.robots.push({key: 2, value: 'r2d2'});
|
||||
});
|
||||
|
||||
expect(selectCtrl.hasOption('c3p0')).toBe(true);
|
||||
expect(selectCtrl.hasOption('r2d2')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -454,6 +487,39 @@ describe('select', function() {
|
||||
expect(element).toBeValid();
|
||||
expect(element).toBeDirty();
|
||||
});
|
||||
|
||||
describe('selectController.hasOption', function() {
|
||||
it('should return true for options added via ngOptions', function() {
|
||||
scope.robots = [
|
||||
{key: 1, value: 'c3p0'},
|
||||
{key: 2, value: 'r2d2'}
|
||||
];
|
||||
scope.robot = 'r2d2';
|
||||
|
||||
compile('<select ng-model="robot" multiple ' +
|
||||
'ng-options="item.key as item.value for item in robots">' +
|
||||
'</select>');
|
||||
|
||||
var selectCtrl = element.data().$selectController;
|
||||
|
||||
expect(selectCtrl.hasOption('c3p0')).toBe(true);
|
||||
expect(selectCtrl.hasOption('r2d2')).toBe(true);
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.robots.pop();
|
||||
});
|
||||
|
||||
expect(selectCtrl.hasOption('c3p0')).toBe(true);
|
||||
expect(selectCtrl.hasOption('r2d2')).toBe(false);
|
||||
|
||||
scope.$apply(function() {
|
||||
scope.robots.push({key: 2, value: 'r2d2'});
|
||||
});
|
||||
|
||||
expect(selectCtrl.hasOption('c3p0')).toBe(true);
|
||||
expect(selectCtrl.hasOption('r2d2')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user