Compare commits
38 Commits
g3-v1.0.0-rc2
...
v0.9.x
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f128c9619 | |||
| f7a5f1788a | |||
| b3ed7a8a7a | |||
| 62d34e1437 | |||
| 855971c385 | |||
| 2d489ff936 | |||
| fc5cda2f72 | |||
| 05ad1ce90c | |||
| 5703984d4d | |||
| ea8952177e | |||
| 90ca6f983e | |||
| 57030bb6b4 | |||
| 710a27030e | |||
| 19aa16c8d5 | |||
| 4a1972c71b | |||
| 6aa04b1db4 | |||
| ac6e1306ec | |||
| e004378d10 | |||
| 4ec1d8ee86 | |||
| c37bfde9eb | |||
| f6bcbb53f0 | |||
| 53a4580d95 | |||
| 4c8eaa1eb0 | |||
| 4ba35eb97e | |||
| 6fb4bf4c54 | |||
| cc604b6e26 | |||
| 99ee6f275a | |||
| 21c4919a5b | |||
| 714759100c | |||
| ee8e981c47 | |||
| 05e2c3196c | |||
| 718ebf1fcf | |||
| 2f5d17f3b6 | |||
| fd792de9e8 | |||
| a01cf6d39e | |||
| f93e9bfa81 | |||
| 590cd14ae0 | |||
| 74db92cd9f |
+40
-6
@@ -1,9 +1,42 @@
|
||||
<a name="0.9.19"><a/>
|
||||
# 0.9.19 canine-psychokinesis (2011-08-20) #
|
||||
|
||||
## Features
|
||||
- added error handling support for JSONP requests (see error callback param of the [$xhr] service)
|
||||
([commit](https://github.com/angular/angular.js/commit/05e2c3196c857402a9aa93837b565e0a2736af23))
|
||||
- exposed http response headers in the [$xhr] and [$resource] callbacks
|
||||
([commit](https://github.com/angular/angular.js/commit/4ec1d8ee86e3138fb91543ca0dca28463895c090)
|
||||
contributed by Karl Seamon)
|
||||
- added `reloadOnSearch` [$route] param support to prevent unnecessary controller reloads and
|
||||
resulting flicker
|
||||
([commit](https://github.com/angular/angular.js/commit/e004378d100ce767a1107180102790a9a360644e))
|
||||
|
||||
|
||||
## Fixes
|
||||
- make ng:class-even/odd compatible with ng:class
|
||||
(Issue [#508](https://github.com/angular/angular.js/issues/508))
|
||||
- fixed error handling for resources that didn't work in certain situations
|
||||
([commit](https://github.com/angular/angular.js/commit/c37bfde9eb31556ee1eb146795b0c1f1504a4a26)
|
||||
contributed by Karl Seamon)
|
||||
|
||||
|
||||
## Docs
|
||||
- [jsFiddle](http://jsfiddle.net/) integration for all docs.angularjs.org examples (contributed by
|
||||
Dan Doyon).
|
||||
|
||||
|
||||
## Breaking Changes
|
||||
- removed [jqLite] show/hide support. See the
|
||||
[commit](https://github.com/angular/angular.js/commit/4c8eaa1eb05ba98d30ff83f4420d6fcd69045d99)
|
||||
message for details. Developers should use jquery or jqLite's `css('display', 'none')` and
|
||||
`css('display', 'block'/'inline'/..)` instead
|
||||
|
||||
|
||||
<a name="0.9.18"><a/>
|
||||
# AngularJS 0.9.18 jiggling-armfat (2011-07-29) #
|
||||
# 0.9.18 jiggling-armfat (2011-07-29) #
|
||||
|
||||
### Features
|
||||
- made angular(.min).js
|
||||
[ECMAScript 5 Strict Mode](https://developer.mozilla.org/en/JavaScript/Strict_mode) compliant
|
||||
- [ECMAScript 5 Strict Mode](https://developer.mozilla.org/en/JavaScript/Strict_mode) compliance
|
||||
- [jqLite]
|
||||
- added `show()`, `hide()` and `eq()` methods to jqlite
|
||||
([commit](https://github.com/angular/angular.js/commit/7a3fdda9650a06792d9278a8cef06d544d49300f))
|
||||
@@ -59,12 +92,13 @@
|
||||
- doubled our e2e test suite by running all angular e2e tests with jqLite in addition to jQuery
|
||||
|
||||
|
||||
### Breaking changes:
|
||||
### Breaking changes
|
||||
- [commit](https://github.com/angular/angular.js/commit/3af1e7ca2ee8c2acd69e5bcbb3ffc1bf51239285)
|
||||
removed support for the `MMMMM` (long month name), use `MMMM` instead. This was done to align
|
||||
Angular with
|
||||
[Unicode Technical Standard #35](http://unicode.org/reports/tr35/#Date_Format_Patterns) used by
|
||||
Closure, as well as, future DOM apis currently being proposed to w3c.
|
||||
- `$xhr.error`'s `request` argument has no `callback` property anymore, use `success` instead
|
||||
|
||||
|
||||
|
||||
@@ -267,8 +301,7 @@
|
||||
- many, but by far not all, docs were updated, improved and cleaned up
|
||||
|
||||
### Features
|
||||
- [`$route`](http://docs.angularjs.org/#!/api/angular.service.$route) service now supports these
|
||||
features:
|
||||
- [$route] service now supports these features:
|
||||
- route not found handling via `#otherwise()`
|
||||
- redirection support via `#when('/foo', {redirectTo: '/bar'})` (including param interpolation)
|
||||
- setting the parent scope for scopes created by the service via `#parent()`
|
||||
@@ -627,6 +660,7 @@ with the `$route` service
|
||||
[$xhr]: http://docs.angularjs.org/#!/api/angular.service.$xhr
|
||||
[$xhr.cache]: http://docs.angularjs.org/#!/api/angular.service.$xhr.cache
|
||||
[$resource]: http://docs.angularjs.org/#!/api/angular.service.$resource
|
||||
[$route]: http://docs.angularjs.org/#!/api/angular.service.$route
|
||||
[$orderBy]: http://docs.angularjs.org/#!/api/angular.Array.orderBy
|
||||
[date]: http://docs.angularjs.org/#!/api/angular.filter.date
|
||||
[number]: http://docs.angularjs.org/#!/api/angular.filter.number
|
||||
|
||||
@@ -261,6 +261,31 @@ task :package => [:clean, :compile, :docs] do
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js")
|
||||
end
|
||||
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/index-jq.html", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js")
|
||||
end
|
||||
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/index-nocache.html", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js")
|
||||
end
|
||||
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/index-jq-nocache.html", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
f.rewind
|
||||
f.write text.sub('angular.min.js', "angular-#{NG_VERSION.full}.min.js")
|
||||
end
|
||||
|
||||
|
||||
File.open("#{pkg_dir}/docs-#{NG_VERSION.full}/docs-scenario.html", File::RDWR) do |f|
|
||||
text = f.read
|
||||
f.truncate 0
|
||||
|
||||
@@ -53,7 +53,7 @@ to retrieve Buzz activity and comments.
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('fetch buzz and expand', function() {
|
||||
xit('fetch buzz and expand', function() {
|
||||
element(':button:contains(fetch)').click();
|
||||
expect(repeater('div.buzz').count()).toBeGreaterThan(0);
|
||||
element('.buzz a:contains(Expand replies):first').click();
|
||||
|
||||
@@ -93,7 +93,7 @@ no connection between the controller and the view.
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should play a game', function(){
|
||||
xit('should play a game', function(){
|
||||
piece(1, 1);
|
||||
expect(binding('nextMove')).toEqual('O');
|
||||
piece(3, 1);
|
||||
|
||||
@@ -67,17 +67,17 @@ __`app/js/controllers.js`.__
|
||||
<pre>
|
||||
...
|
||||
|
||||
function PhoneListCtrl(Phone_) {
|
||||
function PhoneListCtrl(Phone) {
|
||||
this.orderProp = 'age';
|
||||
this.phones = Phone_.query();
|
||||
this.phones = Phone.query();
|
||||
}
|
||||
//PhoneListCtrl.$inject = ['Phone'];
|
||||
|
||||
|
||||
function PhoneDetailCtrl(Phone_) {
|
||||
function PhoneDetailCtrl(Phone) {
|
||||
var self = this;
|
||||
|
||||
self.phone = Phone_.get({phoneId: self.params.phoneId}, function(phone) {
|
||||
self.phone = Phone.get({phoneId: self.params.phoneId}, function(phone) {
|
||||
self.mainImageUrl = phone.images[0];
|
||||
});
|
||||
|
||||
@@ -94,7 +94,7 @@ Notice how in `PhoneListCtrl` we replaced:
|
||||
|
||||
with:
|
||||
|
||||
this.phones = Phone_.query();
|
||||
this.phones = Phone.query();
|
||||
|
||||
This is a simple statement that we want to query for all phones.
|
||||
|
||||
@@ -116,7 +116,7 @@ We have modified our unit tests to verify that our new service is issuing HTTP r
|
||||
processing them as expected. The tests also check that our controllers are interacting with the
|
||||
service correctly.
|
||||
|
||||
The {@link api/angular.service.$resource $resource} client augments the response object with
|
||||
The {@link api/angular.service.$resource $resource} service augments the response object with
|
||||
methods for updating and deleting the resource. If we were to use the standard `toEqual` matcher,
|
||||
our tests would fail because the test values would not match the responses exactly. To solve the
|
||||
problem, we use a newly-defined `toEqualData` {@link
|
||||
|
||||
@@ -89,6 +89,14 @@ describe('ngdoc', function(){
|
||||
'<pre class="doc-source">\n<>\n</pre></doc:example><p>after</p>');
|
||||
});
|
||||
|
||||
it('should preserve the jsfiddle attribute', function(){
|
||||
var doc = new Doc('@description before <doc:example>' +
|
||||
'<doc:source jsfiddle="foo">lala</doc:source></doc:example> after');
|
||||
doc.parse();
|
||||
expect(doc.description).toContain('<p>before </p><doc:example>' +
|
||||
'<pre class="doc-source" jsfiddle="foo">lala</pre></doc:example><p>after</p>');
|
||||
});
|
||||
|
||||
it('should escape <doc:scenario> element', function(){
|
||||
var doc = new Doc('@description before <doc:example>' +
|
||||
'<doc:scenario>\n<>\n</doc:scenario></doc:example> after');
|
||||
|
||||
+11
-4
@@ -43,10 +43,17 @@ function writeTheRest(writesFuture) {
|
||||
writesFuture.push(writer.copyDir('img'));
|
||||
writesFuture.push(writer.copyDir('examples'));
|
||||
writesFuture.push(writer.copyTpl('index.html'));
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html',
|
||||
'build/docs/index-jq.html',
|
||||
'<!-- jquery place holder -->',
|
||||
'<script src=\"jquery.min.js\"><\/script>'));
|
||||
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'build/docs/index-jq.html',
|
||||
'<!-- jquery place holder -->', '<script src=\"jquery.min.js\"><\/script>'));
|
||||
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'build/docs/index-nocache.html',
|
||||
'manifest="appcache.manifest"', ''));
|
||||
|
||||
writesFuture.push(writer.copy('docs/src/templates/index.html', 'build/docs/index-jq-nocache.html',
|
||||
'manifest="appcache.manifest"', '',
|
||||
'<!-- jquery place holder -->', '<script src=\"jquery.min.js\"><\/script>'));
|
||||
|
||||
writesFuture.push(writer.copyTpl('offline.html'));
|
||||
writesFuture.push(writer.copyTpl('docs-scenario.html'));
|
||||
writesFuture.push(writer.copyTpl('jquery.min.js'));
|
||||
|
||||
+9
-7
@@ -111,9 +111,11 @@ Doc.prototype = {
|
||||
'</pre></div>';
|
||||
});
|
||||
} else if (isDocWidget('example')) {
|
||||
text = text.replace(/(<doc:source>)([\s\S]*)(<\/doc:source>)/mi,
|
||||
function(_, before, content, after){
|
||||
return '<pre class="doc-source">' + htmlEscape(content) + '</pre>';
|
||||
text = text.replace(/<doc:source(\s+jsfiddle="[^"]+")?>([\s\S]*)<\/doc:source>/mi,
|
||||
function(_, jsfiddle, content){
|
||||
return '<pre class="doc-source"' + (jsfiddle || '') +'>' +
|
||||
htmlEscape(content) +
|
||||
'</pre>';
|
||||
});
|
||||
text = text.replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi,
|
||||
function(_, before, content, after){
|
||||
@@ -547,15 +549,15 @@ Doc.prototype = {
|
||||
function scenarios(docs){
|
||||
var specs = [];
|
||||
|
||||
specs.push('describe("angular without jquery", function() {');
|
||||
appendSpecs('index.html');
|
||||
specs.push('describe("angular+jqlite", function() {');
|
||||
appendSpecs('index-nocache.html');
|
||||
specs.push('});');
|
||||
|
||||
specs.push('');
|
||||
specs.push('');
|
||||
|
||||
specs.push('describe("angular with jquery", function() {');
|
||||
appendSpecs('index-jq.html');
|
||||
specs.push('describe("angular+jquery", function() {');
|
||||
appendSpecs('index-jq-nocache.html');
|
||||
specs.push('});');
|
||||
|
||||
return specs.join('\n');
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ function collect() {
|
||||
var work;
|
||||
if(/\.js$/.test(file)) {
|
||||
console.log("reading " + file + ".......");
|
||||
work = Q.when(qfs.read(file), function(content) {
|
||||
work = Q.when(qfs.read(file, 'b'), function(content) {
|
||||
processJsFile(content, file).forEach (function(doc) {
|
||||
allDocs.push(doc);
|
||||
});
|
||||
@@ -45,7 +45,7 @@ function collect() {
|
||||
var work2;
|
||||
if (file.match(/\.ngdoc$/)) {
|
||||
console.log("reading " + file + ".......");
|
||||
work2 = Q.when(qfs.read(file), function(content){
|
||||
work2 = Q.when(qfs.read(file, 'b'), function(content){
|
||||
var section = '@section ' + file.split('/')[2] + '\n';
|
||||
allDocs.push(new ngdoc.Doc(section + content.toString(),file, 1).parse());
|
||||
});
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
@namespace doc url("http://docs.angularjs.org/");
|
||||
|
||||
doc\:example {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul.doc-example {
|
||||
list-style-type: none;
|
||||
position: relative;
|
||||
@@ -25,6 +19,37 @@ ul.doc-example > li.doc-example-heading {
|
||||
margin-bottom: -10px;
|
||||
}
|
||||
|
||||
span.nojsfiddle {
|
||||
float: right;
|
||||
font-size: 14px;
|
||||
margin-right:10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
form.jsfiddle {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
form.jsfiddle button {
|
||||
cursor: pointer;
|
||||
padding: 4px 10px;
|
||||
margin: 10px;
|
||||
background-color: #FFF;
|
||||
font-weight: bold;
|
||||
color: #7989D6;
|
||||
border-color: #7989D6;
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-border-radius:8px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
form.jsfiddle textarea, form.jsfiddle input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
li.doc-example-live {
|
||||
padding: 10px;
|
||||
font-size: 1.2em;
|
||||
|
||||
@@ -23,12 +23,12 @@
|
||||
|
||||
angular.widget('doc:example', function(element){
|
||||
this.descend(true); //compile the example code
|
||||
element.hide();
|
||||
|
||||
//jQuery find() methods in this widget contain primitive selectors on purpose so that we can use
|
||||
//jqlite instead. jqlite's find() method currently supports onlt getElementsByTagName!
|
||||
var example = element.find('pre').eq(0), //doc-source
|
||||
exampleSrc = example.text(),
|
||||
jsfiddle = example.attr('jsfiddle') || true,
|
||||
scenario = element.find('pre').eq(1); //doc-scenario
|
||||
|
||||
var code = indent(exampleSrc);
|
||||
@@ -36,7 +36,8 @@
|
||||
'<ul class="doc-example">' +
|
||||
'<li class="doc-example-heading"><h3>Source</h3></li>' +
|
||||
'<li class="doc-example-source" ng:non-bindable>' +
|
||||
'<pre class="brush: js; html-script: true; highlight: [' +
|
||||
jsFiddleButton(jsfiddle) + // may or may not have value
|
||||
'<pre class="brush: js; html-script: true; highlight: [' +
|
||||
code.hilite + ']; toolbar: false;"></pre></li>' +
|
||||
'<li class="doc-example-heading"><h3>Live Preview</h3></li>' +
|
||||
'<li class="doc-example-live">' + exampleSrc +'</li>';
|
||||
@@ -53,14 +54,57 @@
|
||||
|
||||
element.html('');
|
||||
element.append(tabs);
|
||||
element.show();
|
||||
|
||||
var script = (exampleSrc.match(/<script[^\>]*>([\s\S]*)<\/script>/) || [])[1] || '';
|
||||
|
||||
try {
|
||||
window.eval(script);
|
||||
if (window.execScript) { // IE
|
||||
window.execScript(script || '"stupid IE!"'); // IE complains when evaling empty string
|
||||
} else {
|
||||
window.eval(script);
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
|
||||
function jsFiddleButton(jsfiddle) {
|
||||
if (jsfiddle !== 'false') {
|
||||
if(jsfiddle == true) {
|
||||
//dynamically generate a fiddle
|
||||
var fiddleUrl = 'http://jsfiddle.net/api/post/library/pure/',
|
||||
fiddleSrc = exampleSrc,
|
||||
stripIndent = fiddleSrc.match(/^(\s*)/)[1].length;
|
||||
|
||||
//escape closing textarea
|
||||
fiddleSrc = fiddleSrc.replace(/<\/textarea>/gi,'</textarea>')
|
||||
//strip extra indentation
|
||||
fiddleSrc = fiddleSrc.replace(new RegExp('^\\s{' + stripIndent + '}', 'gm'), '');
|
||||
|
||||
return '<form class="jsfiddle" method="post" action="' + fiddleUrl + '" target="_blank">' +
|
||||
'<textarea name="css">' +
|
||||
'body { font-family: Arial,Helvetica,sans-serif; }\n' +
|
||||
'body, td, th { font-size: 14px; margin: 0; }\n' +
|
||||
'table { border-collapse: separate; border-spacing: 2px; display: table; margin-bottom: 0; margin-top: 0; -moz-box-sizing: border-box; text-indent: 0; }\n' +
|
||||
'a:link, a:visited, a:hover { color: #5D6DB6; text-decoration: none; }\n' +
|
||||
'</textarea>' +
|
||||
'<input type="text" name="title" value="AngularJS Live Example">' +
|
||||
'<textarea name="html">' +
|
||||
'<script src="' + angularJsUrl + '" ng:autobind></script>\n\n' +
|
||||
'<!-- AngularJS Example Code: -->\n\n' +
|
||||
fiddleSrc +
|
||||
'</textarea>' +
|
||||
'<button>edit at jsFiddle</button>' +
|
||||
'</form>';
|
||||
} else {
|
||||
//use existing fiddle
|
||||
fiddleUrl = "http://jsfiddle.net" + jsfiddle;
|
||||
return '<form class="jsfiddle" method="get" action="' + fiddleUrl + '" target="_blank">' +
|
||||
'<button>edit at jsFiddle</button>' +
|
||||
'</form>';
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
function indent(text) {
|
||||
@@ -142,7 +186,6 @@
|
||||
'</div>';
|
||||
|
||||
angular.widget('doc:tutorial-instructions', function(element) {
|
||||
element.hide();
|
||||
this.descend(true);
|
||||
|
||||
var tabs = angular.element(HTML_TPL.replace('{show}', element.attr('show') || 'false')),
|
||||
@@ -168,7 +211,6 @@
|
||||
|
||||
element.html('');
|
||||
element.append(tabs);
|
||||
element.show();
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -289,7 +289,8 @@ li {
|
||||
}
|
||||
|
||||
|
||||
#content img {
|
||||
#content img:not(.ng-directive) {
|
||||
/* the negation rule above is to avoid applying this rule to images in buzz and other examples */
|
||||
display: block;
|
||||
margin: 2em auto;
|
||||
padding: 1em;
|
||||
@@ -327,7 +328,6 @@ li {
|
||||
/* subpages */
|
||||
|
||||
#fader {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
@@ -340,10 +340,6 @@ li {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#subpage {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#subpage > div {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
|
||||
@@ -83,8 +83,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="fader" ng:show="subpage"></div>
|
||||
<div id="subpage" ng:show="subpage">
|
||||
<div id="fader" ng:show="subpage" style="display: none"></div>
|
||||
<div id="subpage" ng:show="subpage" style="display: none">
|
||||
<div>
|
||||
<h2>Would you like full offline support for this AngularJS Docs App?</h2>
|
||||
<a ng:click="subpage=false">✕</a>
|
||||
|
||||
+17
-4
@@ -44,12 +44,25 @@ exports.copyTpl = function(filename) {
|
||||
return exports.copy('docs/src/templates/' + filename, OUTPUT_DIR + filename);
|
||||
};
|
||||
|
||||
exports.copy = function (from, to, replacementKey, replacement) {
|
||||
exports.copy = function (from, to) {
|
||||
var args = [].slice.call(arguments);
|
||||
|
||||
args.shift(); // drop 'from'
|
||||
args.shift(); // drop 'to'
|
||||
|
||||
// Have to use rb (read binary), char 'r' is infered by library.
|
||||
return qfs.read(from,'b').then(function(content) {
|
||||
if(replacementKey && replacement) {
|
||||
content = content.toString().replace(replacementKey, replacement);
|
||||
var replacementKey,
|
||||
replacement;
|
||||
|
||||
while (args.length) {
|
||||
replacementKey = args.shift();
|
||||
replacement = args.shift();
|
||||
if(replacementKey != undefined && replacement != undefined) {
|
||||
content = content.toString().replace(replacementKey, replacement);
|
||||
}
|
||||
}
|
||||
|
||||
qfs.write(to, content);
|
||||
});
|
||||
}
|
||||
@@ -83,7 +96,7 @@ function merge(srcs, to) {
|
||||
srcs.forEach(function (src) {
|
||||
done = Q.when(done, function(content) {
|
||||
if(content) contents.push(content);
|
||||
return qfs.read(src);
|
||||
return qfs.read(src, 'b');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -2200,7 +2200,8 @@ jasmine.util.inherit(jasmine.WaitsForBlock, jasmine.Block);
|
||||
jasmine.WaitsForBlock.TIMEOUT_INCREMENT = 10;
|
||||
|
||||
jasmine.WaitsForBlock.prototype.execute = function(onComplete) {
|
||||
this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
|
||||
// (i): disabled this log since its annoying
|
||||
//this.env.reporter.log('>> Jasmine waiting for ' + (this.message || 'something to happen'));
|
||||
var latchFunctionResult;
|
||||
try {
|
||||
latchFunctionResult = this.latchFunction.apply(this.spec);
|
||||
|
||||
+2
-3
@@ -64,7 +64,6 @@ var _undefined = undefined,
|
||||
$boolean = 'boolean',
|
||||
$console = 'console',
|
||||
$date = 'date',
|
||||
$display = 'display',
|
||||
$element = 'element',
|
||||
$function = 'function',
|
||||
$length = 'length',
|
||||
@@ -145,7 +144,7 @@ var _undefined = undefined,
|
||||
* @param {Object|Array} obj Object to iterate over.
|
||||
* @param {function()} iterator Iterator function.
|
||||
* @param {Object} context Object to become context (`this`) for the iterator function.
|
||||
* @returns {Objet|Array} Reference to `obj`.
|
||||
* @returns {Object|Array} Reference to `obj`.
|
||||
*/
|
||||
function forEach(obj, iterator, context) {
|
||||
var key;
|
||||
@@ -875,7 +874,7 @@ function toKeyValue(obj) {
|
||||
|
||||
|
||||
/**
|
||||
* we need our custom mehtod because encodeURIComponent is too agressive and doesn't follow
|
||||
* We need our custom mehtod because encodeURIComponent is too agressive and doesn't follow
|
||||
* http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
|
||||
* segments:
|
||||
* segment = *pchar
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
var browserSingleton;
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc service
|
||||
* @name angular.service.$browser
|
||||
* @requires $log
|
||||
*
|
||||
* @description
|
||||
* Represents the browser.
|
||||
*/
|
||||
|
||||
angularService('$browser', function($log){
|
||||
if (!browserSingleton) {
|
||||
browserSingleton = new Browser(window, jqLite(window.document), jqLite(window.document.body),
|
||||
|
||||
+59
-14
@@ -84,7 +84,9 @@ function Browser(window, document, body, XHR, $log) {
|
||||
* @param {string} method Requested method (get|post|put|delete|head|json)
|
||||
* @param {string} url Requested url
|
||||
* @param {?string} post Post data to send (null if nothing to post)
|
||||
* @param {function(number, string)} callback Function that will be called on response
|
||||
* @param {function(number, string, function([string]))} callback Function that will be called on
|
||||
* response. The third argument is a function that can be called to return a specified response
|
||||
* header or an Object containing all headers (when called with no arguments).
|
||||
* @param {object=} header additional HTTP headers to send with XHR.
|
||||
* Standard headers are:
|
||||
* <ul>
|
||||
@@ -97,15 +99,24 @@ function Browser(window, document, body, XHR, $log) {
|
||||
* Send ajax request
|
||||
*/
|
||||
self.xhr = function(method, url, post, callback, headers) {
|
||||
var parsedHeaders;
|
||||
|
||||
outstandingRequestCount ++;
|
||||
if (lowercase(method) == 'json') {
|
||||
var callbackId = ("angular_" + Math.random() + '_' + (idCounter++)).replace(/\d\./, '');
|
||||
var script = self.addJs(url.replace('JSON_CALLBACK', callbackId));
|
||||
window[callbackId] = function(data){
|
||||
window[callbackId] = function(data) {
|
||||
window[callbackId].data = data;
|
||||
};
|
||||
|
||||
var script = self.addJs(url.replace('JSON_CALLBACK', callbackId), null, function() {
|
||||
if (window[callbackId].data) {
|
||||
completeOutstandingRequest(callback, 200, window[callbackId].data);
|
||||
} else {
|
||||
completeOutstandingRequest(callback);
|
||||
}
|
||||
delete window[callbackId];
|
||||
body[0].removeChild(script);
|
||||
completeOutstandingRequest(callback, 200, data);
|
||||
};
|
||||
});
|
||||
} else {
|
||||
var xhr = new XHR();
|
||||
xhr.open(method, url, true);
|
||||
@@ -115,8 +126,35 @@ function Browser(window, document, body, XHR, $log) {
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
|
||||
var status = xhr.status == 1223 ? 204 : xhr.status || 200;
|
||||
completeOutstandingRequest(callback, status, xhr.responseText);
|
||||
var status = xhr.status == 1223 ? 204 : xhr.status;
|
||||
completeOutstandingRequest(callback, status, xhr.responseText, function(header) {
|
||||
header = lowercase(header);
|
||||
|
||||
if (header) {
|
||||
return parsedHeaders
|
||||
? parsedHeaders[header] || null
|
||||
: xhr.getResponseHeader(header);
|
||||
} else {
|
||||
// Return an object containing each response header
|
||||
parsedHeaders = {};
|
||||
|
||||
forEach(xhr.getAllResponseHeaders().split('\n'), function(line) {
|
||||
var i = line.indexOf(':'),
|
||||
key = lowercase(trim(line.substr(0, i))),
|
||||
value = trim(line.substr(i + 1));
|
||||
|
||||
if (parsedHeaders[key]) {
|
||||
// Combine repeated headers
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
|
||||
parsedHeaders[key] += ', ' + value;
|
||||
} else {
|
||||
parsedHeaders[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return parsedHeaders;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
xhr.send(post || '');
|
||||
@@ -124,11 +162,9 @@ function Browser(window, document, body, XHR, $log) {
|
||||
};
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
* @ngdoc method
|
||||
* @name angular.service.$browser#notifyWhenNoOutstandingRequests
|
||||
* @methodOf angular.service.$browser
|
||||
*
|
||||
* @private
|
||||
* Note: this method is used only by scenario runner
|
||||
* TODO(vojta): prefix this method with $$ ?
|
||||
* @param {function()} callback Function that will be called when no outstanding request
|
||||
*/
|
||||
self.notifyWhenNoOutstandingRequests = function(callback) {
|
||||
@@ -239,7 +275,7 @@ function Browser(window, document, body, XHR, $log) {
|
||||
* The listener gets called with either HashChangeEvent object or simple object that also contains
|
||||
* `oldURL` and `newURL` properties.
|
||||
*
|
||||
* NOTE: this api is intended for use only by the $location service. Please use the
|
||||
* Note: this api is intended for use only by the $location service. Please use the
|
||||
* {@link angular.service.$location $location service} to monitor hash changes in angular apps.
|
||||
*
|
||||
* @param {function(event)} listener Listener function to be called when url hash changes.
|
||||
@@ -452,7 +488,7 @@ function Browser(window, document, body, XHR, $log) {
|
||||
* @description
|
||||
* Adds a script tag to the head.
|
||||
*/
|
||||
self.addJs = function(url, domId) {
|
||||
self.addJs = function(url, domId, done) {
|
||||
// we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
|
||||
// - fetches local scripts via XHR and evals them
|
||||
// - adds and immediately removes script elements from the document
|
||||
@@ -465,6 +501,15 @@ function Browser(window, document, body, XHR, $log) {
|
||||
script.type = 'text/javascript';
|
||||
script.src = url;
|
||||
if (domId) script.id = domId;
|
||||
|
||||
if (msie) {
|
||||
script.onreadystatechange = function() {
|
||||
/loaded|complete/.test(script.readyState) && done && done();
|
||||
};
|
||||
} else {
|
||||
if (done) script.onload = script.onerror = done;
|
||||
}
|
||||
|
||||
body[0].appendChild(script);
|
||||
|
||||
return script;
|
||||
|
||||
+3
-3
@@ -89,7 +89,7 @@ Template.prototype = {
|
||||
* The compilation is a process of walking the DOM tree and trying to match DOM elements to
|
||||
* {@link angular.markup markup}, {@link angular.attrMarkup attrMarkup},
|
||||
* {@link angular.widget widgets}, and {@link angular.directive directives}. For each match it
|
||||
* executes coresponding markup, attrMarkup, widget or directive template function and collects the
|
||||
* executes corresponding markup, attrMarkup, widget or directive template function and collects the
|
||||
* instance functions into a single template function which is then returned.
|
||||
*
|
||||
* The template function can then be used once to produce the view or as it is the case with
|
||||
@@ -118,7 +118,7 @@ Template.prototype = {
|
||||
* root scope is created.
|
||||
* * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
|
||||
* `template` and call the `cloneAttachFn` function allowing the caller to attach the
|
||||
* cloned elements to the DOM document at the approriate place. The `cloneAttachFn` is
|
||||
* cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
|
||||
* called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
|
||||
*
|
||||
* * `clonedElement` - is a clone of the original `element` passed into the compiler.
|
||||
@@ -234,7 +234,7 @@ Compiler.prototype = {
|
||||
* not a problem, but under some circumstances the values for data
|
||||
* is not available until after the full view is computed. If such
|
||||
* values are needed before they are computed the order of
|
||||
* evaluation can be change using ng:eval-order
|
||||
* evaluation can be changed using ng:eval-order
|
||||
*
|
||||
* @element ANY
|
||||
* @param {integer|string=} [priority=0] priority integer, or FIRST, LAST constant
|
||||
|
||||
+10
-3
@@ -76,9 +76,16 @@ ResourceFactory.prototype = {
|
||||
case 4:
|
||||
error = a4;
|
||||
success = a3;
|
||||
//fallthrough
|
||||
case 3:
|
||||
case 2:
|
||||
if (isFunction(a2)) {
|
||||
if (isFunction(a1)) {
|
||||
success = a1;
|
||||
error = a2;
|
||||
break;
|
||||
}
|
||||
|
||||
success = a2;
|
||||
error = a3;
|
||||
//fallthrough
|
||||
@@ -102,9 +109,9 @@ ResourceFactory.prototype = {
|
||||
var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
|
||||
self.xhr(
|
||||
action.method,
|
||||
route.url(extend({}, action.params || {}, extractParams(data), params)),
|
||||
route.url(extend({}, extractParams(data), action.params || {}, params)),
|
||||
data,
|
||||
function(status, response) {
|
||||
function(status, response, responseHeaders) {
|
||||
if (response) {
|
||||
if (action.isArray) {
|
||||
value.length = 0;
|
||||
@@ -115,7 +122,7 @@ ResourceFactory.prototype = {
|
||||
copy(response, value);
|
||||
}
|
||||
}
|
||||
(success||noop)(value);
|
||||
(success||noop)(value, responseHeaders);
|
||||
},
|
||||
error || action.verifyCache,
|
||||
action.verifyCache);
|
||||
|
||||
+3
-3
@@ -9,7 +9,7 @@ function getter(instance, path, unboundFn) {
|
||||
for ( var i = 0; i < len; i++) {
|
||||
key = element[i];
|
||||
if (!key.match(/^[\$\w][\$\w\d]*$/))
|
||||
throw "Expression '" + path + "' is not a valid expression for accesing variables.";
|
||||
throw "Expression '" + path + "' is not a valid expression for accessing variables.";
|
||||
if (instance) {
|
||||
lastInstance = instance;
|
||||
instance = instance[key];
|
||||
@@ -202,7 +202,7 @@ function createScope(parent, providers, instanceCache) {
|
||||
* @description
|
||||
* Assigns a value to a property of the current scope specified via `property_chain`. Unlike in
|
||||
* JavaScript, if there are any `undefined` intermediary properties, empty objects are created
|
||||
* and assigned in to them instead of throwing an exception.
|
||||
* and assigned to them instead of throwing an exception.
|
||||
*
|
||||
<pre>
|
||||
var scope = angular.scope();
|
||||
@@ -368,7 +368,7 @@ function createScope(parent, providers, instanceCache) {
|
||||
* parameters, `newValue` and `oldValue`.
|
||||
* @param {(function()|DOMElement)=} [exceptionHanlder=angular.service.$exceptionHandler] Handler
|
||||
* that gets called when `watchExp` or `listener` throws an exception. If a DOMElement is
|
||||
* specified as handler, the element gets decorated by angular with the information about the
|
||||
* specified as a handler, the element gets decorated by angular with the information about the
|
||||
* exception.
|
||||
* @param {boolean=} [initRun=true] Flag that prevents the first execution of the listener upon
|
||||
* registration.
|
||||
|
||||
Vendored
+1
-1
@@ -76,7 +76,7 @@
|
||||
|
||||
// load the js scripts
|
||||
for (i in Array.prototype.slice.call(arguments, 0)) {
|
||||
file = arguments[i];
|
||||
var file = arguments[i];
|
||||
document.write('<script type="text/javascript" src="' + serverPath + file + '" ' +
|
||||
'onload="angularClobberTest(\'' + file + '\')"></script>');
|
||||
}
|
||||
|
||||
Vendored
+28
-11
@@ -152,7 +152,14 @@ function MockBrowser() {
|
||||
throw new Error("Missing HTTP request header: " + key + ": " + value);
|
||||
}
|
||||
});
|
||||
callback(expectation.code, expectation.response);
|
||||
callback(expectation.code, expectation.response, function(header) {
|
||||
if (header) {
|
||||
header = header.toLowerCase();
|
||||
return expectation.responseHeaders && expectation.responseHeaders[header] || null;
|
||||
} else {
|
||||
return expectation.responseHeaders || {};
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
self.xhr.expectations = expectations;
|
||||
@@ -162,12 +169,22 @@ function MockBrowser() {
|
||||
if (data && angular.isString(data)) url += "|" + data;
|
||||
var expect = expectations[method] || (expectations[method] = {});
|
||||
return {
|
||||
respond: function(code, response) {
|
||||
respond: function(code, response, responseHeaders) {
|
||||
if (!angular.isNumber(code)) {
|
||||
responseHeaders = response;
|
||||
response = code;
|
||||
code = 200;
|
||||
}
|
||||
expect[url] = {code:code, response:response, headers: headers || {}};
|
||||
angular.forEach(responseHeaders, function(value, key) {
|
||||
delete responseHeaders[key];
|
||||
responseHeaders[key.toLowerCase()] = value;
|
||||
});
|
||||
expect[url] = {
|
||||
code: code,
|
||||
response: response,
|
||||
headers: headers || {},
|
||||
responseHeaders: responseHeaders || {}
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -268,7 +285,7 @@ function MockBrowser() {
|
||||
self.defer = function(fn, delay) {
|
||||
delay = delay || 0;
|
||||
self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId});
|
||||
self.deferredFns.sort(function(a,b){ return a.time - b.time;});
|
||||
self.deferredFns.sort(function(a,b){return a.time - b.time;});
|
||||
return self.deferredNextId++;
|
||||
};
|
||||
|
||||
@@ -279,11 +296,11 @@ function MockBrowser() {
|
||||
self.defer.cancel = function(deferId) {
|
||||
var fnIndex;
|
||||
|
||||
forEach(self.deferredFns, function(fn, index) {
|
||||
angular.forEach(self.deferredFns, function(fn, index) {
|
||||
if (fn.id === deferId) fnIndex = index;
|
||||
});
|
||||
|
||||
if (fnIndex) {
|
||||
if (fnIndex !== undefined) {
|
||||
self.deferredFns.splice(fnIndex, 1);
|
||||
}
|
||||
};
|
||||
@@ -374,7 +391,7 @@ angular.service('$browser', function(){
|
||||
* See {@link angular.mock} for more info on angular mocks.
|
||||
*/
|
||||
angular.service('$exceptionHandler', function() {
|
||||
return function(e) { throw e;};
|
||||
return function(e) {throw e;};
|
||||
});
|
||||
|
||||
|
||||
@@ -394,10 +411,10 @@ angular.service('$log', MockLogFactory);
|
||||
|
||||
function MockLogFactory() {
|
||||
var $log = {
|
||||
log: function(){ $log.log.logs.push(arguments); },
|
||||
warn: function(){ $log.warn.logs.push(arguments); },
|
||||
info: function(){ $log.info.logs.push(arguments); },
|
||||
error: function(){ $log.error.logs.push(arguments); }
|
||||
log: function(){$log.log.logs.push(arguments);},
|
||||
warn: function(){$log.warn.logs.push(arguments);},
|
||||
info: function(){$log.info.logs.push(arguments);},
|
||||
error: function(){$log.error.logs.push(arguments);}
|
||||
};
|
||||
|
||||
$log.log.logs = [];
|
||||
|
||||
+10
-5
@@ -265,7 +265,7 @@ angularDirective("ng:bind", function(expression, element){
|
||||
error = formatError(e);
|
||||
});
|
||||
this.$element = oldElement;
|
||||
// If we are HTML than save the raw HTML data so that we don't
|
||||
// If we are HTML then save the raw HTML data so that we don't
|
||||
// recompute sanitization since it is expensive.
|
||||
// TODO: turn this into a more generic way to compute this
|
||||
if (isHtml = (value instanceof HTML))
|
||||
@@ -571,9 +571,14 @@ function ngClass(selector) {
|
||||
var existing = element[0].className + ' ';
|
||||
return function(element){
|
||||
this.$onEval(function(){
|
||||
if (selector(this.$index)) {
|
||||
var value = this.$eval(expression);
|
||||
var scope = this;
|
||||
|
||||
if (selector(scope.$index)) {
|
||||
var ngClassVal = scope.$eval(element.attr('ng:class') || '');
|
||||
if (isArray(ngClassVal)) ngClassVal = ngClassVal.join(' ');
|
||||
var value = scope.$eval(expression);
|
||||
if (isArray(value)) value = value.join(' ');
|
||||
if (ngClassVal && ngClassVal !== value) value = value + ' ' + ngClassVal;
|
||||
element[0].className = trim(existing + value);
|
||||
}
|
||||
}, element);
|
||||
@@ -733,7 +738,7 @@ angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;}));
|
||||
angularDirective("ng:show", function(expression, element){
|
||||
return function(element){
|
||||
this.$onEval(function(){
|
||||
toBoolean(this.$eval(expression)) ? element.show() : element.hide();
|
||||
element.css('display', toBoolean(this.$eval(expression)) ? '' : 'none');
|
||||
}, element);
|
||||
};
|
||||
});
|
||||
@@ -774,7 +779,7 @@ angularDirective("ng:show", function(expression, element){
|
||||
angularDirective("ng:hide", function(expression, element){
|
||||
return function(element){
|
||||
this.$onEval(function(){
|
||||
toBoolean(this.$eval(expression)) ? element.hide() : element.show();
|
||||
element.css('display', toBoolean(this.$eval(expression)) ? 'none' : '');
|
||||
}, element);
|
||||
};
|
||||
});
|
||||
|
||||
+5
-5
@@ -40,10 +40,10 @@
|
||||
*
|
||||
* @param {number} amount Input to filter.
|
||||
* @param {string=} symbol Currency symbol or identifier to be displayed.
|
||||
* @returns {string} Formated number.
|
||||
* @returns {string} Formatted number.
|
||||
*
|
||||
* @css ng-format-negative
|
||||
* When the value is negative, this css class is applied to the binding making it by default red.
|
||||
* When the value is negative, this css class is applied to the binding making it (by default) red.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
@@ -82,7 +82,7 @@ angularFilter.currency = function(amount, currencySymbol){
|
||||
* @description
|
||||
* Formats a number as text.
|
||||
*
|
||||
* If the input is not a number empty string is returned.
|
||||
* If the input is not a number an empty string is returned.
|
||||
*
|
||||
* @param {number|string} number Number to format.
|
||||
* @param {(number|string)=} [fractionSize=2] Number of decimal places to round the number to.
|
||||
@@ -492,7 +492,7 @@ angularFilter.uppercase = uppercase;
|
||||
*
|
||||
* The input is sanitized by parsing the html into tokens. All safe tokens (from a whitelist) are
|
||||
* then serialized back to properly escaped html string. This means that no unsafe input can make
|
||||
* it into the returned string, however since our parser is more strict than a typical browser
|
||||
* it into the returned string, however, since our parser is more strict than a typical browser
|
||||
* parser, it's possible that some obscure input, which would be recognized as valid HTML by a
|
||||
* browser, won't make it through the sanitizer.
|
||||
*
|
||||
@@ -581,7 +581,7 @@ angularFilter.html = function(html, option){
|
||||
*
|
||||
* @description
|
||||
* Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
|
||||
* plane email address links.
|
||||
* plain email address links.
|
||||
*
|
||||
* @param {string} text Input text.
|
||||
* @returns {string} Html-linkified text.
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
* @name angular.formatter
|
||||
* @description
|
||||
*
|
||||
* Formatters are used for translating data formats between those used in for display and those used
|
||||
* Formatters are used for translating data formats between those used for display and those used
|
||||
* for storage.
|
||||
*
|
||||
* Following is the list of built-in angular formatters:
|
||||
|
||||
+2
-30
@@ -23,7 +23,7 @@
|
||||
* focus on the most commonly needed functionality and minimal footprint. For this reason only a
|
||||
* limited number of jQuery methods, arguments and invocation styles are supported.
|
||||
*
|
||||
* NOTE: All element references in angular are always wrapped with jQuery (lite) and are never
|
||||
* Note: All element references in angular are always wrapped with jQuery (lite) and are never
|
||||
* raw DOM references.
|
||||
*
|
||||
* ## Angular's jQuery lite implements these functions:
|
||||
@@ -47,8 +47,6 @@
|
||||
* - [text()](http://api.jquery.com/text/)
|
||||
* - [trigger()](http://api.jquery.com/trigger/)
|
||||
* - [eq()](http://api.jquery.com/eq/)
|
||||
* - [show()](http://api.jquery.com/show/)
|
||||
* - [hide()](http://api.jquery.com/hide/)
|
||||
*
|
||||
* ## Additionally these methods extend the jQuery and are available in both jQuery and jQuery lite
|
||||
* version:
|
||||
@@ -152,7 +150,7 @@ function JQLiteData(element, key, value) {
|
||||
|
||||
function JQLiteHasClass(element, selector, _) {
|
||||
// the argument '_' is important, since it makes the function have 3 arguments, which
|
||||
// is neede for delegate function to realize the this is a getter.
|
||||
// is needed for delegate function to realize the this is a getter.
|
||||
var className = " " + selector + " ";
|
||||
return ((" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf( className ) > -1);
|
||||
}
|
||||
@@ -456,32 +454,6 @@ forEach({
|
||||
return element.getElementsByTagName(selector);
|
||||
},
|
||||
|
||||
hide: function(element) {
|
||||
if (element.style) {
|
||||
if(element.style.display !=="none" && !JQLiteData(element,"olddisplay")) {
|
||||
JQLiteData( element, "olddisplay", element.style.display);
|
||||
}
|
||||
element.style.display = "none";
|
||||
}
|
||||
},
|
||||
|
||||
show: function(element) {
|
||||
if(element.style) {
|
||||
var display = element.style.display;
|
||||
if ( display === "" || display === "none" ) {
|
||||
|
||||
// restore the original value overwritten by hide if present or default to nothing (which
|
||||
// will let browser correctly choose between 'inline' or 'block')
|
||||
element.style.display = JQLiteData(element, "olddisplay") || "";
|
||||
|
||||
// if the previous didn't make the element visible then there are some cascading rules that
|
||||
// are still hiding it, so let's default to 'block', which might be incorrect in case of
|
||||
// elmenents that should be 'inline' by default, but oh well :-)
|
||||
if (!isVisible([element])) element.style.display = "block";
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
clone: JQLiteClone
|
||||
}, function(fn, name){
|
||||
/**
|
||||
|
||||
+14
-9
@@ -12,8 +12,8 @@
|
||||
* Markup extensions do not themselves produce linking functions. Think of markup as a way to
|
||||
* produce shorthand for a {@link angular.widget widget} or a {@link angular.directive directive}.
|
||||
*
|
||||
* The most prominent example of an markup in angular is the built-in double curly markup
|
||||
* `{{expression}}`, which is a shorthand for `<span ng:bind="expression"></span>`.
|
||||
* The most prominent example of a markup in angular is the built-in double curly markup
|
||||
* `{{expression}}`, which is shorthand for `<span ng:bind="expression"></span>`.
|
||||
*
|
||||
* Create custom markup like this:
|
||||
*
|
||||
@@ -34,7 +34,7 @@
|
||||
* @description
|
||||
*
|
||||
* Attribute markup extends the angular compiler in a very similar way as {@link angular.markup}
|
||||
* except that it allows you to modify the state of the attribute text rather then the content of a
|
||||
* except that it allows you to modify the state of the attribute text rather than the content of a
|
||||
* node.
|
||||
*
|
||||
* Create custom attribute markup like this:
|
||||
@@ -138,7 +138,7 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
*
|
||||
* @description
|
||||
* Using <angular/> markup like {{hash}} in an href attribute makes
|
||||
* the page open to a wrong URL, ff the user clicks that link before
|
||||
* the page open to a wrong URL, if the user clicks that link before
|
||||
* angular has a chance to replace the {{hash}} with actual URL, the
|
||||
* link will be broken and will most likely return a 404 error.
|
||||
* The `ng:href` solves this problem by placing the `href` in the
|
||||
@@ -251,7 +251,8 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
* </div>
|
||||
* </pre>
|
||||
*
|
||||
* the HTML specs do not require browsers preserve the special attributes such as disabled.(The presense of them means true and absense means false)
|
||||
* The HTML specs do not require browsers to preserve the special attributes such as disabled.
|
||||
* (The presence of them means true and absence means false)
|
||||
* This prevents the angular compiler from correctly retrieving the binding expression.
|
||||
* To solve this problem, we introduce ng:disabled.
|
||||
*
|
||||
@@ -281,7 +282,8 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
* @name angular.directive.ng:checked
|
||||
*
|
||||
* @description
|
||||
* the HTML specs do not require browsers preserve the special attributes such as checked.(The presense of them means true and absense means false)
|
||||
* The HTML specs do not require browsers to preserve the special attributes such as checked.
|
||||
* (The presence of them means true and absence means false)
|
||||
* This prevents the angular compiler from correctly retrieving the binding expression.
|
||||
* To solve this problem, we introduce ng:checked.
|
||||
* @example
|
||||
@@ -310,7 +312,8 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
* @name angular.directive.ng:multiple
|
||||
*
|
||||
* @description
|
||||
* the HTML specs do not require browsers preserve the special attributes such as multiple.(The presense of them means true and absense means false)
|
||||
* The HTML specs do not require browsers to preserve the special attributes such as multiple.
|
||||
* (The presence of them means true and absence means false)
|
||||
* This prevents the angular compiler from correctly retrieving the binding expression.
|
||||
* To solve this problem, we introduce ng:multiple.
|
||||
*
|
||||
@@ -345,7 +348,8 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
* @name angular.directive.ng:readonly
|
||||
*
|
||||
* @description
|
||||
* the HTML specs do not require browsers preserve the special attributes such as readonly.(The presense of them means true and absense means false)
|
||||
* The HTML specs do not require browsers to preserve the special attributes such as readonly.
|
||||
* (The presence of them means true and absence means false)
|
||||
* This prevents the angular compiler from correctly retrieving the binding expression.
|
||||
* To solve this problem, we introduce ng:readonly.
|
||||
* @example
|
||||
@@ -374,7 +378,8 @@ angularTextMarkup('option', function(text, textNode, parentElement){
|
||||
* @name angular.directive.ng:selected
|
||||
*
|
||||
* @description
|
||||
* the HTML specs do not require browsers preserve the special attributes such as selected.(The presense of them means true and absense means false)
|
||||
* The HTML specs do not require browsers to preserve the special attributes such as selected.
|
||||
* (The presence of them means true and absence means false)
|
||||
* This prevents the angular compiler from correctly retrieving the binding expression.
|
||||
* To solve this problem, we introduce ng:selected.
|
||||
* @example
|
||||
|
||||
@@ -147,9 +147,15 @@
|
||||
*
|
||||
<pre>
|
||||
var User = $resource('/user/:userId', {userId:'@id'});
|
||||
User.get({userId:123}, function(u){
|
||||
User.get({userId:123}, function(u, responseHeaders){
|
||||
u.abc = true;
|
||||
u.$save();
|
||||
u.$save(function(u, responseHeaders) {
|
||||
// Get an Object containing all response headers
|
||||
var allHeaders = responseHeaders();
|
||||
|
||||
// Get a specific response header
|
||||
u.newId = responseHeaders('Location');
|
||||
});
|
||||
});
|
||||
</pre>
|
||||
|
||||
@@ -157,7 +163,7 @@
|
||||
|
||||
Let's look at what a buzz client created with the `$resource` service looks like:
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<doc:source jsfiddle="false">
|
||||
<script>
|
||||
function BuzzController($resource) {
|
||||
this.Activity = $resource(
|
||||
|
||||
+31
-6
@@ -22,7 +22,7 @@
|
||||
Try changing the URL in the input box to see changes.
|
||||
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<doc:source jsfiddle="false">
|
||||
<script>
|
||||
function MainCntl($route, $location) {
|
||||
this.$route = $route;
|
||||
@@ -68,6 +68,8 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
matcher = switchRouteMatcher,
|
||||
parentScope = this,
|
||||
dirty = 0,
|
||||
lastHashPath,
|
||||
lastRouteParams,
|
||||
$route = {
|
||||
routes: routes,
|
||||
|
||||
@@ -136,6 +138,18 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
* The custom `redirectTo` function is expected to return a string which will be used
|
||||
* to update `$location.hash`.
|
||||
*
|
||||
* - `[reloadOnSearch=true]` - {boolean=} - reload route when $location.hashSearch
|
||||
* changes. If this option is disabled, you should set up a $watch to be notified of
|
||||
* param (hashSearch) changes as follows:
|
||||
*
|
||||
* function MyCtrl($route) {
|
||||
* this.$watch(function() {
|
||||
* return $route.current.params.myHashSearchParam;
|
||||
* }, function(params) {
|
||||
* //do stuff with params
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* @returns {Object} route object
|
||||
*
|
||||
* @description
|
||||
@@ -144,8 +158,8 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
when:function (path, params) {
|
||||
if (isUndefined(path)) return routes; //TODO(im): remove - not needed!
|
||||
var route = routes[path];
|
||||
if (!route) route = routes[path] = {};
|
||||
if (params) extend(route, params);
|
||||
if (!route) route = routes[path] = {reloadOnSearch: true};
|
||||
if (params) extend(route, params); //TODO(im): what the heck? merge two route definitions?
|
||||
dirty++;
|
||||
return route;
|
||||
},
|
||||
@@ -183,14 +197,16 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
|
||||
|
||||
function switchRouteMatcher(on, when, dstName) {
|
||||
var regex = '^' + when.replace(/[\.\\\(\)\^\$]/g, "\$1") + '$',
|
||||
// TODO(i): this code is convoluted and inefficient, we should construct the route matching
|
||||
// regex only once and then reuse it
|
||||
var regex = '^' + when.replace(/([\.\\\(\)\^\$])/g, "\\$1") + '$',
|
||||
params = [],
|
||||
dst = {};
|
||||
forEach(when.split(/\W/), function(param){
|
||||
if (param) {
|
||||
var paramRegExp = new RegExp(":" + param + "([\\W])");
|
||||
if (regex.match(paramRegExp)) {
|
||||
regex = regex.replace(paramRegExp, "([^\/]*)$1");
|
||||
regex = regex.replace(paramRegExp, "([^\\/]*)$1");
|
||||
params.push(param);
|
||||
}
|
||||
}
|
||||
@@ -209,6 +225,14 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
function updateRoute(){
|
||||
var childScope, routeParams, pathParams, segmentMatch, key, redir;
|
||||
|
||||
if ($route.current) {
|
||||
if (!$route.current.reloadOnSearch && (lastHashPath == location.hashPath)) {
|
||||
$route.current.params = extend({}, location.hashSearch, lastRouteParams);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lastHashPath = location.hashPath;
|
||||
$route.current = null;
|
||||
forEach(routes, function(rParams, rPath) {
|
||||
if (!pathParams) {
|
||||
@@ -255,6 +279,7 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
scope: childScope,
|
||||
params: extend({}, location.hashSearch, pathParams)
|
||||
});
|
||||
lastRouteParams = pathParams;
|
||||
}
|
||||
|
||||
//fire onChange callbacks
|
||||
@@ -266,7 +291,7 @@ angularServiceInject('$route', function(location, $updateView) {
|
||||
}
|
||||
|
||||
|
||||
this.$watch(function(){return dirty + location.hash;}, updateRoute);
|
||||
this.$watch(function(){ return dirty + location.hash; }, updateRoute);
|
||||
|
||||
return $route;
|
||||
}, ['$location', '$updateView']);
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
* or 'XHR' (instead of {@link angular.service.$xhr}) then you may be changing the model
|
||||
* without angular knowledge and you may need to call '$updateView()' directly.
|
||||
*
|
||||
* NOTE: if you wish to update the view immediately (without delay), you can do so by calling
|
||||
* Note: if you wish to update the view immediately (without delay), you can do so by calling
|
||||
* {@link angular.scope.$eval} at any time from your code:
|
||||
* <pre>scope.$root.$eval()</pre>
|
||||
*
|
||||
|
||||
@@ -48,13 +48,14 @@ angularServiceInject('$xhr.bulk', function($xhr, $error, $log){
|
||||
queue.requests = [];
|
||||
queue.callbacks = [];
|
||||
$xhr('POST', url, {requests: currentRequests},
|
||||
function(code, response) {
|
||||
function(code, response, responseHeaders) {
|
||||
forEach(response, function(response, i) {
|
||||
try {
|
||||
if (response.status == 200) {
|
||||
(currentRequests[i].success || noop)(response.status, response.response);
|
||||
(currentRequests[i].success || noop)
|
||||
(response.status, response.response, responseHeaders);
|
||||
} else if (isFunction(currentRequests[i].error)) {
|
||||
currentRequests[i].error(response.status, response.response);
|
||||
currentRequests[i].error(response.status, response.response, responseHeaders);
|
||||
} else {
|
||||
$error(currentRequests[i], response);
|
||||
}
|
||||
@@ -64,11 +65,11 @@ angularServiceInject('$xhr.bulk', function($xhr, $error, $log){
|
||||
});
|
||||
(success || noop)();
|
||||
},
|
||||
function(code, response) {
|
||||
function(code, response, responseHeaders) {
|
||||
forEach(currentRequests, function(request, i) {
|
||||
try {
|
||||
if (isFunction(request.error)) {
|
||||
request.error(code, response);
|
||||
request.error(code, response, responseHeaders);
|
||||
} else {
|
||||
$error(request, response);
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
* @param {string} method HTTP method.
|
||||
* @param {string} url Destination URL.
|
||||
* @param {(string|Object)=} post Request body.
|
||||
* @param {function(number, (string|Object))} success Response success callback.
|
||||
* @param {function(number, (string|Object))=} error Response error callback.
|
||||
* @param {function(number, (string|Object), Function)} success Response success callback.
|
||||
* @param {function(number, (string|Object), Function)} error Response error callback.
|
||||
* @param {boolean=} [verifyCache=false] If `true` then a result is immediately returned from cache
|
||||
* (if present) while a request is sent to the server for a fresh response that will update the
|
||||
* cached entry. The `success` function will be called when the response is received.
|
||||
@@ -55,9 +55,9 @@ angularServiceInject('$xhr.cache', function($xhr, $defer, $error, $log) {
|
||||
if (dataCached = cache.data[url]) {
|
||||
|
||||
if (sync) {
|
||||
success(200, copy(dataCached.value));
|
||||
success(200, copy(dataCached.value), copy(dataCached.headers));
|
||||
} else {
|
||||
$defer(function() { success(200, copy(dataCached.value)); });
|
||||
$defer(function() { success(200, copy(dataCached.value), copy(dataCached.headers)); });
|
||||
}
|
||||
|
||||
if (!verifyCache)
|
||||
@@ -70,20 +70,20 @@ angularServiceInject('$xhr.cache', function($xhr, $defer, $error, $log) {
|
||||
} else {
|
||||
inflight[url] = {successes: [success], errors: [error]};
|
||||
cache.delegate(method, url, post,
|
||||
function(status, response) {
|
||||
function(status, response, responseHeaders) {
|
||||
if (status == 200)
|
||||
cache.data[url] = {value: response};
|
||||
cache.data[url] = {value: response, headers: responseHeaders};
|
||||
var successes = inflight[url].successes;
|
||||
delete inflight[url];
|
||||
forEach(successes, function(success) {
|
||||
try {
|
||||
(success||noop)(status, copy(response));
|
||||
(success||noop)(status, copy(response), responseHeaders);
|
||||
} catch(e) {
|
||||
$log.error(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
function(status, response) {
|
||||
function(status, response, responseHeaders) {
|
||||
var errors = inflight[url].errors,
|
||||
successes = inflight[url].successes;
|
||||
delete inflight[url];
|
||||
@@ -91,7 +91,7 @@ angularServiceInject('$xhr.cache', function($xhr, $defer, $error, $log) {
|
||||
forEach(errors, function(error, i) {
|
||||
try {
|
||||
if (isFunction(error)) {
|
||||
error(status, copy(response));
|
||||
error(status, copy(response), copy(responseHeaders));
|
||||
} else {
|
||||
$error(
|
||||
{method: method, url: url, data: post, success: successes[i]},
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
* - `method` – `{string}` – The http request method.
|
||||
* - `url` – `{string}` – The request destination.
|
||||
* - `data` – `{(string|Object)=} – An optional request body.
|
||||
* - `callback` – `{function()}` – The callback function
|
||||
* - `success` – `{function()}` – The success callback function
|
||||
*
|
||||
* @param {Object} response Response object.
|
||||
*
|
||||
|
||||
+47
-16
@@ -6,7 +6,7 @@
|
||||
* @name angular.service.$xhr
|
||||
* @function
|
||||
* @requires $browser $xhr delegates all XHR requests to the `$browser.xhr()`. A mock version
|
||||
* of the $browser exists which allows setting expectaitions on XHR requests
|
||||
* of the $browser exists which allows setting expectations on XHR requests
|
||||
* in your tests
|
||||
* @requires $xhr.error $xhr delegates all non `2xx` response code to this service.
|
||||
* @requires $log $xhr delegates all exceptions to `$log.error()`.
|
||||
@@ -83,7 +83,7 @@
|
||||
* cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the server
|
||||
* can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure that only
|
||||
* JavaScript running on your domain could have read the token. The token must be unique for each
|
||||
* user and must be verifiable by the server (to prevent the JavaScript making up its own tokens).
|
||||
* user and must be verifiable by the server (to prevent the JavaScript making up its own tokens).
|
||||
* We recommend that the token is a digest of your site's authentication cookie with
|
||||
* {@link http://en.wikipedia.org/wiki/Rainbow_table salt for added security}.
|
||||
*
|
||||
@@ -96,7 +96,7 @@
|
||||
* angular generated callback function.
|
||||
* @param {(string|Object)=} post Request content as either a string or an object to be stringified
|
||||
* as JSON before sent to the server.
|
||||
* @param {function(number, (string|Object))} success A function to be called when the response is
|
||||
* @param {function(number, (string|Object), Function)} success A function to be called when the response is
|
||||
* received. The success function will be called with:
|
||||
*
|
||||
* - {number} code [HTTP status code](http://en.wikipedia.org/wiki/List_of_HTTP_status_codes) of
|
||||
@@ -104,27 +104,35 @@
|
||||
* {@link angular.service.$xhr.error} service (or custom error callback).
|
||||
* - {string|Object} response Response object as string or an Object if the response was in JSON
|
||||
* format.
|
||||
* - {function(string=)} responseHeaders A function that when called with a {string} header name,
|
||||
* returns the value of that header or null if it does not exist; when called without
|
||||
* arguments, returns an object containing every response header
|
||||
* @param {function(number, (string|Object))} error A function to be called if the response code is
|
||||
* not 2xx.. Accepts the same arguments as success, above.
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<doc:source jsfiddle="false">
|
||||
<script>
|
||||
function FetchCntl($xhr) {
|
||||
var self = this;
|
||||
|
||||
this.fetch = function() {
|
||||
self.clear();
|
||||
self.code = null;
|
||||
self.response = null;
|
||||
|
||||
$xhr(self.method, self.url, function(code, response) {
|
||||
self.code = code;
|
||||
self.response = response;
|
||||
}, function(code, response) {
|
||||
self.code = code;
|
||||
self.response = response || "Request failed";
|
||||
});
|
||||
};
|
||||
|
||||
this.clear = function() {
|
||||
self.code = null;
|
||||
self.response = null;
|
||||
this.updateModel = function(method, url) {
|
||||
self.method = method;
|
||||
self.url = url;
|
||||
};
|
||||
}
|
||||
FetchCntl.$inject = ['$xhr'];
|
||||
@@ -134,15 +142,38 @@
|
||||
<option>GET</option>
|
||||
<option>JSON</option>
|
||||
</select>
|
||||
<input type="text" name="url" value="index.html" size="80"/><br/>
|
||||
<button ng:click="fetch()">fetch</button>
|
||||
<button ng:click="clear()">clear</button>
|
||||
<a href="" ng:click="method='GET'; url='index.html'">sample</a>
|
||||
<a href="" ng:click="method='JSON'; url='https://www.googleapis.com/buzz/v1/activities/googlebuzz/@self?alt=json&callback=JSON_CALLBACK'">buzz</a>
|
||||
<input type="text" name="url" value="index.html" size="80"/>
|
||||
<button ng:click="fetch()">fetch</button><br>
|
||||
<button ng:click="updateModel('GET', 'index.html')">Sample GET</button>
|
||||
<button ng:click="updateModel('JSON', 'http://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">Sample JSONP</button>
|
||||
<button ng:click="updateModel('JSON', 'http://angularjs.org/doesntexist&callback=JSON_CALLBACK')">Invalid JSONP</button>
|
||||
<pre>code={{code}}</pre>
|
||||
<pre>response={{response}}</pre>
|
||||
</div>
|
||||
</doc:source>
|
||||
<doc:scenario>
|
||||
it('should make xhr GET request', function() {
|
||||
element(':button:contains("Sample GET")').click();
|
||||
element(':button:contains("fetch")').click();
|
||||
expect(binding('code')).toBe('code=200');
|
||||
expect(binding('response')).toMatch(/angularjs.org/);
|
||||
});
|
||||
|
||||
it('should make JSONP request to the angularjs.org', function() {
|
||||
element(':button:contains("Sample JSONP")').click();
|
||||
element(':button:contains("fetch")').click();
|
||||
expect(binding('code')).toBe('code=200');
|
||||
expect(binding('response')).toMatch(/Super Hero!/);
|
||||
});
|
||||
|
||||
it('should make JSONP request to invalid URL and invoke the error handler',
|
||||
function() {
|
||||
element(':button:contains("Invalid JSONP")').click();
|
||||
element(':button:contains("fetch")').click();
|
||||
expect(binding('code')).toBe('code=');
|
||||
expect(binding('response')).toBe('response=Request failed');
|
||||
});
|
||||
</doc:scenario>
|
||||
</doc:example>
|
||||
*/
|
||||
angularServiceInject('$xhr', function($browser, $error, $log, $updateView){
|
||||
@@ -170,7 +201,7 @@ angularServiceInject('$xhr', function($browser, $error, $log, $updateView){
|
||||
post = toJson(post);
|
||||
}
|
||||
|
||||
$browser.xhr(method, url, post, function(code, response){
|
||||
$browser.xhr(method, url, post, function(code, response, responseHeaders){
|
||||
try {
|
||||
if (isString(response)) {
|
||||
if (response.match(/^\)\]\}',\n/)) response=response.substr(6);
|
||||
@@ -179,9 +210,9 @@ angularServiceInject('$xhr', function($browser, $error, $log, $updateView){
|
||||
}
|
||||
}
|
||||
if (200 <= code && code < 300) {
|
||||
success(code, response);
|
||||
success(code, response, responseHeaders);
|
||||
} else if (isFunction(error)) {
|
||||
error(code, response);
|
||||
error(code, response, responseHeaders);
|
||||
} else {
|
||||
$error(
|
||||
{method: method, url: url, data: post, success: success},
|
||||
|
||||
+2
-1
@@ -287,7 +287,8 @@ extend(angularValidator, {
|
||||
if (value.match(/^\+\d{2,3} (\(\d{1,5}\))?[\d ]+\d$/)) {
|
||||
return null;
|
||||
}
|
||||
return "Phone number needs to be in 1(987)654-3210 format in North America or +999 (123) 45678 906 internationaly.";
|
||||
return "Phone number needs to be in 1(987)654-3210 format in North America " +
|
||||
"or +999 (123) 45678 906 internationally.";
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
+11
-16
@@ -6,7 +6,7 @@
|
||||
* @name angular.widget
|
||||
* @description
|
||||
*
|
||||
* An angular widget can be either a custom attribute that modifies an existing DOM elements or an
|
||||
* An angular widget can be either a custom attribute that modifies an existing DOM element or an
|
||||
* entirely new DOM element.
|
||||
*
|
||||
* During html compilation, widgets are processed after {@link angular.markup markup}, but before
|
||||
@@ -39,7 +39,7 @@
|
||||
* @description
|
||||
* The most common widgets you will use will be in the form of the
|
||||
* standard HTML set. These widgets are bound using the `name` attribute
|
||||
* to an expression. In addition they can have `ng:validate`, `ng:required`,
|
||||
* to an expression. In addition, they can have `ng:validate`, `ng:required`,
|
||||
* `ng:format`, `ng:change` attribute to further control their behavior.
|
||||
*
|
||||
* @usageContent
|
||||
@@ -292,7 +292,7 @@ function compileFormatter(expr) {
|
||||
*
|
||||
* @description
|
||||
* The `ng:format` attribute widget formats stored data to user-readable text and parses the text
|
||||
* back to the stored form. You might find this useful for example if you collect user input in a
|
||||
* back to the stored form. You might find this useful, for example, if you collect user input in a
|
||||
* text field but need to store the data in the model as a list. Check out
|
||||
* {@link angular.formatter formatters} to learn more.
|
||||
*
|
||||
@@ -437,7 +437,7 @@ function noopAccessor() { return { get: noop, set: noop }; }
|
||||
/*
|
||||
* TODO: refactor
|
||||
*
|
||||
* The table bellow is not quite right. In some cases the formatter is on the model side
|
||||
* The table below is not quite right. In some cases the formatter is on the model side
|
||||
* and in some cases it is on the view side. This is a historical artifact
|
||||
*
|
||||
* The concept of model/view accessor is useful for anyone who is trying to develop UI, and
|
||||
@@ -447,16 +447,11 @@ function noopAccessor() { return { get: noop, set: noop }; }
|
||||
*
|
||||
*/
|
||||
var textWidget = inputWidget('keydown change', modelAccessor, valueAccessor, initWidgetValue(), true),
|
||||
buttonWidget = inputWidget('click', noopAccessor, noopAccessor, noop),
|
||||
INPUT_TYPE = {
|
||||
'text': textWidget,
|
||||
'textarea': textWidget,
|
||||
'hidden': textWidget,
|
||||
'password': textWidget,
|
||||
'button': buttonWidget,
|
||||
'submit': buttonWidget,
|
||||
'reset': buttonWidget,
|
||||
'image': buttonWidget,
|
||||
'checkbox': inputWidget('click', modelFormattedAccessor, checkedAccessor, initWidgetValue(false)),
|
||||
'radio': inputWidget('click', modelFormattedAccessor, radioAccessor, radioInit),
|
||||
'select-one': inputWidget('change', modelAccessor, valueAccessor, initWidgetValue(null)),
|
||||
@@ -567,7 +562,7 @@ function inputWidgetSelector(element){
|
||||
|
||||
angularWidget('input', inputWidgetSelector);
|
||||
angularWidget('textarea', inputWidgetSelector);
|
||||
angularWidget('button', inputWidgetSelector);
|
||||
|
||||
|
||||
/**
|
||||
* @workInProgress
|
||||
@@ -738,7 +733,7 @@ angularWidget('select', function(element){
|
||||
var optionGroup,
|
||||
collection = valuesFn(scope) || [],
|
||||
key = selectElement.val(),
|
||||
tempScope = scope.$new(),
|
||||
tempScope = inherit(scope),
|
||||
value, optionElement, index, groupIndex, length, groupLength;
|
||||
|
||||
try {
|
||||
@@ -796,7 +791,7 @@ angularWidget('select', function(element){
|
||||
fragment,
|
||||
groupIndex, index,
|
||||
optionElement,
|
||||
optionScope = scope.$new(),
|
||||
optionScope = inherit(scope),
|
||||
modelValue = model.get(),
|
||||
selected,
|
||||
selectedSet = false, // nothing is selected yet
|
||||
@@ -960,7 +955,7 @@ angularWidget('select', function(element){
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<doc:source jsfiddle="false">
|
||||
<select name="url">
|
||||
<option value="examples/ng-include/template1.html">template1.html</option>
|
||||
<option value="examples/ng-include/template2.html">template2.html</option>
|
||||
@@ -1341,12 +1336,12 @@ angularWidget('@ng:repeat', function(expression, element){
|
||||
* Sometimes it is necessary to write code which looks like bindings but which should be left alone
|
||||
* by angular. Use `ng:non-bindable` to make angular ignore a chunk of HTML.
|
||||
*
|
||||
* NOTE: `ng:non-bindable` looks like a directive, but is actually an attribute widget.
|
||||
* Note: `ng:non-bindable` looks like a directive, but is actually an attribute widget.
|
||||
*
|
||||
* @element ANY
|
||||
*
|
||||
* @example
|
||||
* In this example there are two location where a siple binding (`{{}}`) is present, but the one
|
||||
* In this example there are two location where a simple binding (`{{}}`) is present, but the one
|
||||
* wrapped in `ng:non-bindable` is left alone.
|
||||
*
|
||||
* @example
|
||||
@@ -1394,7 +1389,7 @@ angularWidget("@ng:non-bindable", noop);
|
||||
*
|
||||
* @example
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
<doc:source jsfiddle="false">
|
||||
<script>
|
||||
function MyCtrl($route) {
|
||||
$route.when('/overview',
|
||||
|
||||
+117
-21
@@ -49,6 +49,13 @@ describe('browser', function(){
|
||||
this.send = function(post){
|
||||
xhr.post = post;
|
||||
};
|
||||
this.getResponseHeader = function(header) {
|
||||
return header;
|
||||
};
|
||||
this.getAllResponseHeaders = function() {
|
||||
return 'Content-Type: application/json\n\rContent-Encoding: gzip\n\rContent-Type: text/json';
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
logs = {log:[], warn:[], info:[], error:[]};
|
||||
@@ -87,27 +94,81 @@ describe('browser', function(){
|
||||
|
||||
describe('xhr', function(){
|
||||
describe('JSON', function(){
|
||||
it('should add script tag for request', function() {
|
||||
var callback = jasmine.createSpy('callback');
|
||||
var log = "";
|
||||
browser.xhr('JSON', 'http://example.org/path?cb=JSON_CALLBACK', null, function(code, data){
|
||||
log += code + ':' + data + ';';
|
||||
});
|
||||
browser.notifyWhenNoOutstandingRequests(callback);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
expect(scripts.length).toEqual(1);
|
||||
var script = scripts[0];
|
||||
var url = script.src.split('?cb=');
|
||||
expect(url[0]).toEqual('http://example.org/path');
|
||||
expect(typeof fakeWindow[url[1]]).toEqual($function);
|
||||
fakeWindow[url[1]]('data');
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(log).toEqual('200:data;');
|
||||
expect(scripts).toEqual(removedScripts);
|
||||
expect(fakeWindow[url[1]]).toBeUndefined();
|
||||
var log;
|
||||
|
||||
function callback(code, data) {
|
||||
log += code + ':' + data + ';';
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
log = "";
|
||||
});
|
||||
|
||||
|
||||
// We don't have unit tests for IE because script.readyState is readOnly.
|
||||
// Instead we run e2e tests on all browsers - see e2e for $xhr.
|
||||
if (!msie) {
|
||||
|
||||
it('should add script tag for JSONP request', function() {
|
||||
var notify = jasmine.createSpy('notify');
|
||||
browser.xhr('JSON', 'http://example.org/path?cb=JSON_CALLBACK', null, callback);
|
||||
browser.notifyWhenNoOutstandingRequests(notify);
|
||||
expect(notify).not.toHaveBeenCalled();
|
||||
expect(scripts.length).toEqual(1);
|
||||
var script = scripts[0];
|
||||
var url = script.src.split('?cb=');
|
||||
expect(url[0]).toEqual('http://example.org/path');
|
||||
expect(typeof fakeWindow[url[1]]).toEqual($function);
|
||||
fakeWindow[url[1]]('data');
|
||||
script.onload();
|
||||
|
||||
expect(notify).toHaveBeenCalled();
|
||||
expect(log).toEqual('200:data;');
|
||||
expect(scripts).toEqual(removedScripts);
|
||||
expect(fakeWindow[url[1]]).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
it('should call callback when script fails to load', function() {
|
||||
browser.xhr('JSON', 'http://example.org/path?cb=JSON_CALLBACK', null, callback);
|
||||
var script = scripts[0];
|
||||
expect(typeof script.onload).toBe($function);
|
||||
expect(typeof script.onerror).toBe($function);
|
||||
script.onerror();
|
||||
|
||||
expect(log).toEqual('undefined:undefined;');
|
||||
});
|
||||
|
||||
|
||||
it('should update the outstandingRequests counter for successful requests', function() {
|
||||
var notify = jasmine.createSpy('notify');
|
||||
browser.xhr('JSON', 'http://example.org/path?cb=JSON_CALLBACK', null, callback);
|
||||
browser.notifyWhenNoOutstandingRequests(notify);
|
||||
expect(notify).not.toHaveBeenCalled();
|
||||
|
||||
var script = scripts[0];
|
||||
var url = script.src.split('?cb=');
|
||||
fakeWindow[url[1]]('data');
|
||||
script.onload();
|
||||
|
||||
expect(notify).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should update the outstandingRequests counter for failed requests', function() {
|
||||
var notify = jasmine.createSpy('notify');
|
||||
browser.xhr('JSON', 'http://example.org/path?cb=JSON_CALLBACK', null, callback);
|
||||
browser.notifyWhenNoOutstandingRequests(notify);
|
||||
expect(notify).not.toHaveBeenCalled();
|
||||
|
||||
scripts[0].onerror();
|
||||
|
||||
expect(notify).toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
it('should normalize IE\'s 1223 status code into 204', function() {
|
||||
var callback = jasmine.createSpy('XHR');
|
||||
|
||||
@@ -144,6 +205,41 @@ describe('browser', function(){
|
||||
expect(code).toEqual(202);
|
||||
expect(response).toEqual('RESPONSE');
|
||||
});
|
||||
|
||||
describe('response headers', function() {
|
||||
it('should return a single response header', function() {
|
||||
var headerA;
|
||||
|
||||
browser.xhr('GET', 'URL', null, function(code, resp, headers) {
|
||||
headerA = headers('A-Header');
|
||||
});
|
||||
|
||||
xhr.status = 200;
|
||||
xhr.responseText = 'RESPONSE';
|
||||
xhr.readyState = 4;
|
||||
xhr.onreadystatechange();
|
||||
|
||||
expect(headerA).toEqual('a-header');
|
||||
});
|
||||
|
||||
it('should return an object containing all response headers', function() {
|
||||
var allHeaders;
|
||||
|
||||
browser.xhr('GET', 'URL', null, function(code, resp, headers) {
|
||||
allHeaders = headers();
|
||||
});
|
||||
|
||||
xhr.status = 200;
|
||||
xhr.responseText = 'RESPONSE';
|
||||
xhr.readyState = 4;
|
||||
xhr.onreadystatechange();
|
||||
|
||||
expect(allHeaders).toEqual({
|
||||
'content-type': 'application/json, text/json',
|
||||
'content-encoding': 'gzip'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('defer', function() {
|
||||
@@ -281,17 +377,17 @@ describe('browser', function(){
|
||||
it('should log warnings when 4kb per cookie storage limit is reached', function() {
|
||||
var i, longVal = '', cookieStr;
|
||||
|
||||
for(i=0; i<4092; i++) {
|
||||
for(i=0; i<4091; i++) {
|
||||
longVal += '+';
|
||||
}
|
||||
|
||||
cookieStr = document.cookie;
|
||||
browser.cookies('x', longVal); //total size 4094-4096, so it should go through
|
||||
browser.cookies('x', longVal); //total size 4093-4096, so it should go through
|
||||
expect(document.cookie).not.toEqual(cookieStr);
|
||||
expect(browser.cookies()['x']).toEqual(longVal);
|
||||
expect(logs.warn).toEqual([]);
|
||||
|
||||
browser.cookies('x', longVal + 'xxx'); //total size 4097-4099, a warning should be logged
|
||||
browser.cookies('x', longVal + 'xxxx'); //total size 4097-4099, a warning should be logged
|
||||
expect(logs.warn).toEqual(
|
||||
[[ "Cookie 'x' possibly not set or overflowed because it was too large (4097 > 4096 " +
|
||||
"bytes)!" ]]);
|
||||
|
||||
+50
-12
@@ -75,6 +75,15 @@ describe("resource", function() {
|
||||
nakedExpect(item).toEqual({id:'abc'});
|
||||
});
|
||||
|
||||
it("should build resource with action default param overriding default param", function(){
|
||||
xhr.expectGET('/Customer/123').respond({id:'abc'});
|
||||
var TypeItem = resource.route('/:type/:typeId', {type: 'Order'},
|
||||
{get: {method: 'GET', params: {type: 'Customer'}}});
|
||||
var item = TypeItem.get({typeId:123});
|
||||
xhr.flush();
|
||||
nakedExpect(item).toEqual({id:'abc'});
|
||||
});
|
||||
|
||||
it("should create resource", function(){
|
||||
xhr.expectPOST('/CreditCard', {name:'misko'}).respond({id:123, name:'misko'});
|
||||
|
||||
@@ -83,7 +92,8 @@ describe("resource", function() {
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
xhr.flush();
|
||||
nakedExpect(cc).toEqual({id:123, name:'misko'});
|
||||
expect(callback).toHaveBeenCalledWith(cc);
|
||||
expect(callback.mostRecentCall.args[0]).toEqual(cc);
|
||||
expect(callback.mostRecentCall.args[1]()).toEqual({});
|
||||
});
|
||||
|
||||
it("should read resource", function(){
|
||||
@@ -94,7 +104,8 @@ describe("resource", function() {
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
xhr.flush();
|
||||
nakedExpect(cc).toEqual({id:123, number:'9876'});
|
||||
expect(callback).toHaveBeenCalledWith(cc);
|
||||
expect(callback.mostRecentCall.args[0]).toEqual(cc);
|
||||
expect(callback.mostRecentCall.args[1]()).toEqual({});
|
||||
});
|
||||
|
||||
it("should read partial resource", function(){
|
||||
@@ -108,7 +119,8 @@ describe("resource", function() {
|
||||
expect(cc.number).not.toBeDefined();
|
||||
cc.$get(callback);
|
||||
xhr.flush();
|
||||
expect(callback).toHaveBeenCalledWith(cc);
|
||||
expect(callback.mostRecentCall.args[0]).toEqual(cc);
|
||||
expect(callback.mostRecentCall.args[1]()).toEqual({});
|
||||
expect(cc.number).toEqual('9876');
|
||||
});
|
||||
|
||||
@@ -129,7 +141,8 @@ describe("resource", function() {
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
xhr.flush();
|
||||
nakedExpect(ccs).toEqual([{id:1}, {id:2}]);
|
||||
expect(callback).toHaveBeenCalledWith(ccs);
|
||||
expect(callback.mostRecentCall.args[0]).toEqual(ccs);
|
||||
expect(callback.mostRecentCall.args[1]()).toEqual({});
|
||||
});
|
||||
|
||||
it("should have all arguments optional", function(){
|
||||
@@ -147,14 +160,16 @@ describe("resource", function() {
|
||||
CreditCard.remove({id:123}, callback);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
xhr.flush();
|
||||
nakedExpect(callback.mostRecentCall.args).toEqual([{}]);
|
||||
nakedExpect(callback.mostRecentCall.args[0]).toEqual({});
|
||||
nakedExpect(callback.mostRecentCall.args[1]()).toEqual({});
|
||||
|
||||
callback.reset();
|
||||
xhr.expectDELETE("/CreditCard/333").respond(204, null);
|
||||
CreditCard.remove({id:333}, callback);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
xhr.flush();
|
||||
nakedExpect(callback.mostRecentCall.args).toEqual([{}]);
|
||||
nakedExpect(callback.mostRecentCall.args[0]).toEqual({});
|
||||
nakedExpect(callback.mostRecentCall.args[1]()).toEqual({});
|
||||
});
|
||||
|
||||
it('should post charge verb', function(){
|
||||
@@ -171,7 +186,7 @@ describe("resource", function() {
|
||||
});
|
||||
|
||||
it('should create on save', function(){
|
||||
xhr.expectPOST('/CreditCard', {name:'misko'}).respond({id:123});
|
||||
xhr.expectPOST('/CreditCard', {name:'misko'}).respond({id: 123}, {foo: 'bar'});
|
||||
var cc = new CreditCard();
|
||||
expect(cc.$get).toBeDefined();
|
||||
expect(cc.$query).toBeDefined();
|
||||
@@ -183,7 +198,9 @@ describe("resource", function() {
|
||||
nakedExpect(cc).toEqual({name:'misko'});
|
||||
xhr.flush();
|
||||
nakedExpect(cc).toEqual({id:123});
|
||||
expect(callback).toHaveBeenCalledWith(cc);
|
||||
expect(callback.mostRecentCall.args[0]).toEqual(cc);
|
||||
expect(callback.mostRecentCall.args[1]('foo')).toEqual('bar');
|
||||
expect(callback.mostRecentCall.args[1]()).toEqual({foo: 'bar'});
|
||||
});
|
||||
|
||||
it('should not mutate the resource object if response contains no body', function(){
|
||||
@@ -244,22 +261,43 @@ describe("resource", function() {
|
||||
|
||||
describe('failure mode', function() {
|
||||
var ERROR_CODE = 500,
|
||||
ERROR_RESPONSE = 'Server Error';
|
||||
ERROR_RESPONSE = 'Server Error',
|
||||
errorCB,
|
||||
headersFn;
|
||||
|
||||
beforeEach(function() {
|
||||
xhr.expectGET('/CreditCard/123').respond(ERROR_CODE, ERROR_RESPONSE);
|
||||
errorCB = jasmine.createSpy().andCallFake(function(code, response, headers) {
|
||||
headersFn = headers;
|
||||
});
|
||||
|
||||
xhr.expectGET('/CreditCard/123').respond(ERROR_CODE, ERROR_RESPONSE, {foo: 'bar'});
|
||||
});
|
||||
|
||||
it('should report error when non 2xx if error callback is not provided', function() {
|
||||
xhr.expectGET('/CreditCard/123').respond(ERROR_CODE, ERROR_RESPONSE);
|
||||
CreditCard.get({id:123});
|
||||
xhr.flush();
|
||||
expect($xhrErr).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call the error callback if provided on non 2xx response', function() {
|
||||
CreditCard.get({id:123}, noop, callback);
|
||||
xhr.expectGET('/CreditCard/123').respond(ERROR_CODE, ERROR_RESPONSE);
|
||||
CreditCard.get({id:123}, callback, errorCB);
|
||||
xhr.flush();
|
||||
expect(callback).toHaveBeenCalledWith(500, ERROR_RESPONSE);
|
||||
expect(errorCB).toHaveBeenCalledWith(500, ERROR_RESPONSE, headersFn);
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
expect($xhrErr).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call the error callback if provided on non 2xx response', function() {
|
||||
xhr.expectGET('/CreditCard').respond(ERROR_CODE, ERROR_RESPONSE, {foo: 'bar'});
|
||||
CreditCard.get(callback, errorCB);
|
||||
xhr.flush();
|
||||
expect(errorCB).toHaveBeenCalledWith(500, ERROR_RESPONSE, headersFn);
|
||||
expect(headersFn('foo')).toBe('bar');
|
||||
expect(headersFn()).toEqual({'foo': 'bar'});
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
expect($xhrErr).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -64,7 +64,8 @@ describe('ValidatorTest', function(){
|
||||
});
|
||||
|
||||
it('Phone', function() {
|
||||
var error = "Phone number needs to be in 1(987)654-3210 format in North America or +999 (123) 45678 906 internationaly.";
|
||||
var error = "Phone number needs to be in 1(987)654-3210 format in North America " +
|
||||
"or +999 (123) 45678 906 internationally.";
|
||||
assertEquals(angular.validator.phone("ab"), error);
|
||||
assertEquals(null, angular.validator.phone("1(408)757-3023"));
|
||||
assertEquals(null, angular.validator.phone("+421 (0905) 933 297"));
|
||||
|
||||
@@ -220,6 +220,50 @@ describe("directive", function(){
|
||||
expect(e2.hasClass('even')).toBeTruthy();
|
||||
});
|
||||
|
||||
|
||||
it('should allow both ng:class and ng:class-odd/even on the same element', function() {
|
||||
var scope = compile('<ul>' +
|
||||
'<li ng:repeat="i in [0,1]" ng:class="\'plainClass\'" ' +
|
||||
'ng:class-odd="\'odd\'" ng:class-even="\'even\'"></li>' +
|
||||
'<ul>');
|
||||
scope.$eval();
|
||||
var e1 = jqLite(element[0].childNodes[1]);
|
||||
var e2 = jqLite(element[0].childNodes[2]);
|
||||
|
||||
expect(e1.hasClass('plainClass')).toBeTruthy();
|
||||
expect(e1.hasClass('odd')).toBeTruthy();
|
||||
expect(e1.hasClass('even')).toBeFalsy();
|
||||
expect(e2.hasClass('plainClass')).toBeTruthy();
|
||||
expect(e2.hasClass('even')).toBeTruthy();
|
||||
expect(e2.hasClass('odd')).toBeFalsy();
|
||||
});
|
||||
|
||||
|
||||
it('should allow both ng:class and ng:class-odd/even with multiple classes', function() {
|
||||
var scope = compile('<ul>' +
|
||||
'<li ng:repeat="i in [0,1]" ng:class="[\'A\', \'B\']" ' +
|
||||
'ng:class-odd="[\'C\', \'D\']" ng:class-even="[\'E\', \'F\']"></li>' +
|
||||
'<ul>');
|
||||
scope.$eval();
|
||||
var e1 = jqLite(element[0].childNodes[1]);
|
||||
var e2 = jqLite(element[0].childNodes[2]);
|
||||
|
||||
expect(e1.hasClass('A')).toBeTruthy();
|
||||
expect(e1.hasClass('B')).toBeTruthy();
|
||||
expect(e1.hasClass('C')).toBeTruthy();
|
||||
expect(e1.hasClass('D')).toBeTruthy();
|
||||
expect(e1.hasClass('E')).toBeFalsy();
|
||||
expect(e1.hasClass('F')).toBeFalsy();
|
||||
|
||||
expect(e2.hasClass('A')).toBeTruthy();
|
||||
expect(e2.hasClass('B')).toBeTruthy();
|
||||
expect(e2.hasClass('E')).toBeTruthy();
|
||||
expect(e2.hasClass('F')).toBeTruthy();
|
||||
expect(e2.hasClass('C')).toBeFalsy();
|
||||
expect(e2.hasClass('D')).toBeFalsy();
|
||||
});
|
||||
|
||||
|
||||
describe('ng:style', function(){
|
||||
it('should set', function(){
|
||||
var scope = compile('<div ng:style="{height: \'40px\'}"></div>');
|
||||
|
||||
@@ -511,62 +511,6 @@ describe('jqLite', function(){
|
||||
});
|
||||
|
||||
|
||||
describe('hide', function() {
|
||||
var element;
|
||||
|
||||
afterEach(function() {
|
||||
if (element) dealoc(element);
|
||||
});
|
||||
|
||||
it('should hide the element', function() {
|
||||
element = jqLite('<div></div>');
|
||||
expect(isCssVisible(element)).toBe(true);
|
||||
element.hide();
|
||||
expect(isCssVisible(element)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('show', function() {
|
||||
var element;
|
||||
|
||||
afterEach(function() {
|
||||
if (element) dealoc(element);
|
||||
element.remove();
|
||||
});
|
||||
|
||||
|
||||
it('should show the element ', function() {
|
||||
element = jqLite('<div></div>');
|
||||
element[0].style.display = 'none';
|
||||
expect(isCssVisible(element)).toBe(false);
|
||||
element.show();
|
||||
expect(isCssVisible(element)).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
it('should show previously hidden element and preserve the display value', function() {
|
||||
element = jqLite('<div style="display:inline">xx</div>');
|
||||
jqLite(document.body).append(element);
|
||||
element.hide();
|
||||
expect(isCssVisible(element)).toBe(false);
|
||||
element.show();
|
||||
expect(element[0].style.display).toBe('inline');
|
||||
expect(isCssVisible(element)).toBe(true);
|
||||
|
||||
element[0].style.display = 'block';
|
||||
element.hide();
|
||||
expect(isCssVisible(element)).toBe(false);
|
||||
element.show();
|
||||
expect(isCssVisible(element)).toBe(true);
|
||||
|
||||
// this totally doesn't make sense, it should be 'block', but jquery (1.4.2+1.6.2) behaves
|
||||
// this way.
|
||||
expect(element[0].style.display).toBe('inline');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('eq', function() {
|
||||
it('should select the nth element ', function() {
|
||||
var element = jqLite('<div><span>aa</span></div><div><span>bb</span></div>');
|
||||
|
||||
+161
-2
@@ -55,14 +55,37 @@ describe('$route', function() {
|
||||
|
||||
|
||||
it('should return fn registered with onChange()', function() {
|
||||
var scope = angular.scope(),
|
||||
$route = scope.$service('$route'),
|
||||
var $route = scope.$service('$route'),
|
||||
fn = function() {};
|
||||
|
||||
expect($route.onChange(fn)).toBe(fn);
|
||||
});
|
||||
|
||||
|
||||
it('should match a route that contains special chars in the path', function() {
|
||||
var $route = scope.$service('$route'),
|
||||
$location = scope.$service('$location');
|
||||
|
||||
$route.when('/$test.23/foo(bar)/:baz', {template: 'test.html'});
|
||||
|
||||
$location.hashPath = '/test';
|
||||
scope.$eval();
|
||||
expect($route.current).toBe(null);
|
||||
|
||||
$location.hashPath = '/$testX23/foo(bar)/222';
|
||||
scope.$eval();
|
||||
expect($route.current).toBe(null);
|
||||
|
||||
$location.hashPath = '/$test.23/foo(bar)/222';
|
||||
scope.$eval();
|
||||
expect($route.current).toBeDefined();
|
||||
|
||||
$location.hashPath = '/$test.23/foo\\(bar)/222';
|
||||
scope.$eval();
|
||||
expect($route.current).toBe(null);
|
||||
});
|
||||
|
||||
|
||||
it('should allow routes to be defined with just templates without controllers', function() {
|
||||
var scope = angular.scope(),
|
||||
$location = scope.$service('$location'),
|
||||
@@ -227,4 +250,140 @@ describe('$route', function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('reloadOnSearch', function() {
|
||||
it('should reload a route when reloadOnSearch is enabled and hashSearch changes', function() {
|
||||
var scope = angular.scope(),
|
||||
$location = scope.$service('$location'),
|
||||
$route = scope.$service('$route'),
|
||||
reloaded = jasmine.createSpy('route reload');
|
||||
|
||||
$route.when('/foo', {controller: FooCtrl});
|
||||
$route.onChange(reloaded);
|
||||
|
||||
function FooCtrl() {
|
||||
reloaded();
|
||||
}
|
||||
|
||||
$location.updateHash('/foo');
|
||||
scope.$eval();
|
||||
expect(reloaded).toHaveBeenCalled();
|
||||
reloaded.reset();
|
||||
|
||||
// trigger reload
|
||||
$location.hashSearch.foo = 'bar';
|
||||
scope.$eval();
|
||||
expect(reloaded).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should not reload a route when reloadOnSearch is disabled and only hashSearch changes',
|
||||
function() {
|
||||
var scope = angular.scope(),
|
||||
$location = scope.$service('$location'),
|
||||
$route = scope.$service('$route'),
|
||||
reloaded = jasmine.createSpy('route reload');
|
||||
|
||||
$route.when('/foo', {controller: FooCtrl, reloadOnSearch: false});
|
||||
$route.onChange(reloaded);
|
||||
|
||||
function FooCtrl() {
|
||||
reloaded();
|
||||
}
|
||||
|
||||
expect(reloaded).not.toHaveBeenCalled();
|
||||
|
||||
$location.updateHash('/foo');
|
||||
scope.$eval();
|
||||
expect(reloaded).toHaveBeenCalled();
|
||||
reloaded.reset();
|
||||
|
||||
// don't trigger reload
|
||||
$location.hashSearch.foo = 'bar';
|
||||
scope.$eval();
|
||||
expect(reloaded).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should reload reloadOnSearch route when url differs only in route path param', function() {
|
||||
var scope = angular.scope(),
|
||||
$location = scope.$service('$location'),
|
||||
$route = scope.$service('$route'),
|
||||
reloaded = jasmine.createSpy('routeReload'),
|
||||
onRouteChange = jasmine.createSpy('onRouteChange');
|
||||
|
||||
$route.when('/foo/:fooId', {controller: FooCtrl, reloadOnSearch: false});
|
||||
$route.onChange(onRouteChange);
|
||||
|
||||
function FooCtrl() {
|
||||
reloaded();
|
||||
}
|
||||
|
||||
expect(reloaded).not.toHaveBeenCalled();
|
||||
expect(onRouteChange).not.toHaveBeenCalled();
|
||||
|
||||
$location.updateHash('/foo/aaa');
|
||||
scope.$eval();
|
||||
expect(reloaded).toHaveBeenCalled();
|
||||
expect(onRouteChange).toHaveBeenCalled();
|
||||
reloaded.reset();
|
||||
onRouteChange.reset();
|
||||
|
||||
$location.updateHash('/foo/bbb');
|
||||
scope.$eval();
|
||||
expect(reloaded).toHaveBeenCalled();
|
||||
expect(onRouteChange).toHaveBeenCalled();
|
||||
reloaded.reset();
|
||||
onRouteChange.reset();
|
||||
|
||||
$location.hashSearch.foo = 'bar';
|
||||
scope.$eval();
|
||||
expect(reloaded).not.toHaveBeenCalled();
|
||||
expect(onRouteChange).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
it('should update route params when reloadOnSearch is disabled and hashSearch', function() {
|
||||
var scope = angular.scope(),
|
||||
$location = scope.$service('$location'),
|
||||
$route = scope.$service('$route'),
|
||||
routeParams = jasmine.createSpy('routeParams');
|
||||
|
||||
$route.when('/foo', {controller: FooCtrl});
|
||||
$route.when('/bar/:barId', {controller: FooCtrl, reloadOnSearch: false});
|
||||
|
||||
function FooCtrl() {
|
||||
this.$watch(function() {
|
||||
return $route.current.params;
|
||||
}, function(params) {
|
||||
routeParams(params);
|
||||
});
|
||||
}
|
||||
|
||||
expect(routeParams).not.toHaveBeenCalled();
|
||||
|
||||
$location.updateHash('/foo');
|
||||
scope.$eval();
|
||||
expect(routeParams).toHaveBeenCalledWith({});
|
||||
routeParams.reset();
|
||||
|
||||
// trigger reload
|
||||
$location.hashSearch.foo = 'bar';
|
||||
scope.$eval();
|
||||
expect(routeParams).toHaveBeenCalledWith({foo: 'bar'});
|
||||
routeParams.reset();
|
||||
|
||||
$location.updateHash('/bar/123');
|
||||
scope.$eval();
|
||||
expect(routeParams).toHaveBeenCalledWith({barId: '123'});
|
||||
routeParams.reset();
|
||||
|
||||
// don't trigger reload
|
||||
$location.hashSearch.foo = 'bar';
|
||||
scope.$eval();
|
||||
$route.current.scope.$eval(); // ng:view propagates evals so we have to do it by hand here
|
||||
expect(routeParams).toHaveBeenCalledWith({barId: '123', foo: 'bar'});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,8 +18,9 @@ describe('$xhr.bulk', function() {
|
||||
});
|
||||
|
||||
|
||||
function callback(code, response) {
|
||||
function callback(code, response, responseHeaders) {
|
||||
expect(code).toEqual(200);
|
||||
expect(responseHeaders()).toEqual({});
|
||||
log = log + toJson(response) + ';';
|
||||
}
|
||||
|
||||
@@ -81,6 +82,8 @@ describe('$xhr.bulk', function() {
|
||||
$browserXhr.flush();
|
||||
|
||||
expect($xhrError).not.toHaveBeenCalled();
|
||||
expect(callback).toHaveBeenCalledWith(404, 'NotFound');
|
||||
expect(callback.mostRecentCall.args[0]).toEqual(404);
|
||||
expect(callback.mostRecentCall.args[1]).toEqual('NotFound');
|
||||
expect(callback.mostRecentCall.args[2]()).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
describe('$xhr.cache', function() {
|
||||
var scope, $browser, $browserXhr, $xhrErr, cache, log;
|
||||
var scope, $browser, $browserXhr, $xhrErr, cache, log, rHeaders;
|
||||
|
||||
beforeEach(function() {
|
||||
scope = angular.scope({}, null, {'$xhr.error': $xhrErr = jasmine.createSpy('$xhr.error')});
|
||||
@@ -9,6 +9,7 @@ describe('$xhr.cache', function() {
|
||||
$browserXhr = $browser.xhr;
|
||||
cache = scope.$service('$xhr.cache');
|
||||
log = '';
|
||||
rHeaders = {};
|
||||
});
|
||||
|
||||
|
||||
@@ -17,18 +18,21 @@ describe('$xhr.cache', function() {
|
||||
});
|
||||
|
||||
|
||||
function callback(code, response) {
|
||||
function callback(code, response, responseHeaders) {
|
||||
expect(code).toEqual(200);
|
||||
expect(responseHeaders()).toEqual(rHeaders);
|
||||
log = log + toJson(response) + ';';
|
||||
}
|
||||
|
||||
|
||||
it('should cache requests', function(){
|
||||
$browserXhr.expectGET('/url').respond('first');
|
||||
rHeaders = {foo: 'bar'};
|
||||
|
||||
$browserXhr.expectGET('/url').respond('first', rHeaders);
|
||||
cache('GET', '/url', null, callback);
|
||||
$browserXhr.flush();
|
||||
|
||||
$browserXhr.expectGET('/url').respond('ERROR');
|
||||
$browserXhr.expectGET('/url').respond('ERROR', rHeaders);
|
||||
cache('GET', '/url', null, callback);
|
||||
$browser.defer.flush();
|
||||
expect(log).toEqual('"first";"first";');
|
||||
@@ -40,11 +44,13 @@ describe('$xhr.cache', function() {
|
||||
|
||||
|
||||
it('should first return cache request, then return server request', function(){
|
||||
$browserXhr.expectGET('/url').respond('first');
|
||||
rHeaders = {foo: 'bar'};
|
||||
|
||||
$browserXhr.expectGET('/url').respond('first', rHeaders);
|
||||
cache('GET', '/url', null, callback, true);
|
||||
$browserXhr.flush();
|
||||
|
||||
$browserXhr.expectGET('/url').respond('ERROR');
|
||||
$browserXhr.expectGET('/url').respond('ERROR', rHeaders);
|
||||
cache('GET', '/url', null, callback, true);
|
||||
$browser.defer.flush();
|
||||
expect(log).toEqual('"first";"first";');
|
||||
@@ -55,7 +61,14 @@ describe('$xhr.cache', function() {
|
||||
|
||||
|
||||
it('should serve requests from cache', function(){
|
||||
cache.data.url = {value:'123'};
|
||||
rHeaders = {foo: 'bar'};
|
||||
|
||||
cache.data.url = {
|
||||
value: '123',
|
||||
headers: function() {
|
||||
return rHeaders;
|
||||
}
|
||||
};
|
||||
cache('GET', 'url', null, callback);
|
||||
$browser.defer.flush();
|
||||
expect(log).toEqual('"123";');
|
||||
@@ -152,13 +165,17 @@ describe('$xhr.cache', function() {
|
||||
|
||||
cache('GET', '/url', null, successSpy, errorSpy, false, true);
|
||||
$browserXhr.flush();
|
||||
expect(errorSpy).toHaveBeenCalledWith(500, 'error');
|
||||
expect(errorSpy.mostRecentCall.args[0]).toEqual(500);
|
||||
expect(errorSpy.mostRecentCall.args[1]).toEqual('error');
|
||||
expect(errorSpy.mostRecentCall.args[2]()).toEqual({});
|
||||
expect(successSpy).not.toHaveBeenCalled();
|
||||
|
||||
errorSpy.reset();
|
||||
cache('GET', '/url', successSpy, errorSpy, false, true);
|
||||
$browserXhr.flush();
|
||||
expect(errorSpy).toHaveBeenCalledWith(500, 'error');
|
||||
expect(errorSpy.mostRecentCall.args[0]).toEqual(500);
|
||||
expect(errorSpy.mostRecentCall.args[1]).toEqual('error');
|
||||
expect(errorSpy.mostRecentCall.args[2]()).toEqual({});
|
||||
expect(successSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
+22
-15
@@ -18,15 +18,16 @@ describe('$xhr', function() {
|
||||
});
|
||||
|
||||
|
||||
function callback(code, response) {
|
||||
log = log + '{code=' + code + '; response=' + toJson(response) + '}';
|
||||
function callback(code, response, responseHeader) {
|
||||
log = log + '{code=' + code + '; response=' + toJson(response) + '; responseHeaders=' +
|
||||
toJson(responseHeader()) + '}';
|
||||
}
|
||||
|
||||
|
||||
it('should forward the request to $browser and decode JSON', function(){
|
||||
$browserXhr.expectGET('/reqGET').respond('first');
|
||||
$browserXhr.expectGET('/reqGETjson').respond('["second"]');
|
||||
$browserXhr.expectPOST('/reqPOST', {post:'data'}).respond('third');
|
||||
$browserXhr.expectGET('/reqGET').respond('first', {h: 'first'});
|
||||
$browserXhr.expectGET('/reqGETjson').respond('["second"]', {h: 'second'});
|
||||
$browserXhr.expectPOST('/reqPOST', {post:'data'}).respond('third', {h: 'third'});
|
||||
|
||||
$xhr('GET', '/reqGET', null, callback);
|
||||
$xhr('GET', '/reqGETjson', null, callback);
|
||||
@@ -35,23 +36,23 @@ describe('$xhr', function() {
|
||||
$browserXhr.flush();
|
||||
|
||||
expect(log).toEqual(
|
||||
'{code=200; response="third"}' +
|
||||
'{code=200; response=["second"]}' +
|
||||
'{code=200; response="first"}');
|
||||
'{code=200; response="third"; responseHeaders={"h":"third"}}' +
|
||||
'{code=200; response=["second"]; responseHeaders={"h":"second"}}' +
|
||||
'{code=200; response="first"; responseHeaders={"h":"first"}}');
|
||||
});
|
||||
|
||||
it('should allow all 2xx requests', function(){
|
||||
$browserXhr.expectGET('/req1').respond(200, '1');
|
||||
$browserXhr.expectGET('/req1').respond(200, '1', {h: '1'});
|
||||
$xhr('GET', '/req1', null, callback);
|
||||
$browserXhr.flush();
|
||||
|
||||
$browserXhr.expectGET('/req2').respond(299, '2');
|
||||
$browserXhr.expectGET('/req2').respond(299, '2', {h: '2'});
|
||||
$xhr('GET', '/req2', null, callback);
|
||||
$browserXhr.flush();
|
||||
|
||||
expect(log).toEqual(
|
||||
'{code=200; response="1"}' +
|
||||
'{code=299; response="2"}');
|
||||
'{code=200; response="1"; responseHeaders={"h":"1"}}' +
|
||||
'{code=299; response="2"; responseHeaders={"h":"2"}}');
|
||||
});
|
||||
|
||||
|
||||
@@ -120,18 +121,24 @@ describe('$xhr', function() {
|
||||
var errorSpy = jasmine.createSpy('error'),
|
||||
successSpy = jasmine.createSpy('success');
|
||||
|
||||
$browserXhr.expectGET('/url').respond(500, 'error');
|
||||
$browserXhr.expectGET('/url').respond(500, 'error', {foo: 'bar'});
|
||||
$xhr('GET', '/url', null, successSpy, errorSpy);
|
||||
$browserXhr.flush();
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(500, 'error');
|
||||
expect(errorSpy.mostRecentCall.args[0]).toEqual(500);
|
||||
expect(errorSpy.mostRecentCall.args[1]).toEqual('error');
|
||||
expect(errorSpy.mostRecentCall.args[2]('foo')).toEqual('bar');
|
||||
expect(errorSpy.mostRecentCall.args[2]()).toEqual({foo: 'bar'});
|
||||
expect(successSpy).not.toHaveBeenCalled();
|
||||
|
||||
errorSpy.reset();
|
||||
$xhr('GET', '/url', successSpy, errorSpy);
|
||||
$browserXhr.flush();
|
||||
|
||||
expect(errorSpy).toHaveBeenCalledWith(500, 'error');
|
||||
expect(errorSpy.mostRecentCall.args[0]).toEqual(500);
|
||||
expect(errorSpy.mostRecentCall.args[1]).toEqual('error');
|
||||
expect(errorSpy.mostRecentCall.args[2]('foo')).toEqual('bar');
|
||||
expect(errorSpy.mostRecentCall.args[2]()).toEqual({foo: 'bar'});
|
||||
expect(successSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@@ -90,6 +90,28 @@ beforeEach(function(){
|
||||
return "Expected " + expected + " to match an Error with message " + toJson(messageRegexp);
|
||||
};
|
||||
return this.actual.name == 'Error' && messageRegexp.test(this.actual.message);
|
||||
},
|
||||
|
||||
toHaveBeenCalledOnce: function() {
|
||||
if (arguments.length > 0) {
|
||||
throw new Error('toHaveBeenCalledOnce does not take arguments, use toHaveBeenCalledWith');
|
||||
}
|
||||
|
||||
if (!jasmine.isSpy(this.actual)) {
|
||||
throw new Error('Expected a spy, but got ' + jasmine.pp(this.actual) + '.');
|
||||
}
|
||||
|
||||
this.message = function() {
|
||||
var msg = 'Expected spy ' + this.actual.identity + ' to have been called once, but was ',
|
||||
count = this.actual.callCount;
|
||||
return [
|
||||
count == 0 ? msg + 'never called.'
|
||||
: msg + 'called ' + count + ' times.',
|
||||
msg.replace('to have', 'not to have') + 'called once.'
|
||||
];
|
||||
};
|
||||
|
||||
return this.actual.callCount == 1;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -293,18 +293,6 @@ describe("widget", function(){
|
||||
expect(scope.$get('name')).toEqual('Kai');
|
||||
});
|
||||
|
||||
it('should call ng:change on button click', function(){
|
||||
compile('<input type="button" value="Click Me" ng:change="clicked = true"/>');
|
||||
browserTrigger(element);
|
||||
expect(scope.$get('clicked')).toEqual(true);
|
||||
});
|
||||
|
||||
it('should support button alias', function(){
|
||||
compile('<button ng:change="clicked = true">Click {{"Me"}}.</button>');
|
||||
browserTrigger(element);
|
||||
expect(scope.$get('clicked')).toEqual(true);
|
||||
expect(scope.$element.text()).toEqual("Click Me.");
|
||||
});
|
||||
|
||||
describe('radio', function(){
|
||||
|
||||
@@ -417,14 +405,6 @@ describe("widget", function(){
|
||||
expect(scope.$service('$log').error.logs.shift()[0]).
|
||||
toMatchError(/Syntax Error: Token '''' is an unexpected token/);
|
||||
});
|
||||
|
||||
it('should report error on ng:change exception', function(){
|
||||
compile('<button ng:change="a-2=x">click</button>');
|
||||
browserTrigger(element);
|
||||
expect(element.hasClass('ng-exception')).toBeTruthy();
|
||||
expect(scope.$service('$log').error.logs.shift()[0]).
|
||||
toMatchError(/Syntax Error: Token '=' implies assignment but \[a-2\] can not be assigned to/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ng:switch', function(){
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
# <angular/> build config file
|
||||
# AngularJS build config file
|
||||
---
|
||||
version: 0.9.18
|
||||
codename: jiggling-armfat
|
||||
version: 0.9.20
|
||||
codename: ???
|
||||
|
||||
Reference in New Issue
Block a user