changed the documentation @example to use <doc:example>

This commit is contained in:
Misko Hevery 2011-01-31 16:21:29 -08:00
parent ed768ebc53
commit ba6b68b6ae
20 changed files with 1841 additions and 1653 deletions

View file

@ -46,12 +46,13 @@ You can use these variables in the function:
the DOM in addition to transforming the input. the DOM in addition to transforming the input.
@exampleDescription @example
The following example filter reverses a text string. In addition, it conditionally makes the The following example filter reverses a text string. In addition, it conditionally makes the
text upper-case (to demonstrate optional arguments) and assigns color (to demonstrate DOM text upper-case (to demonstrate optional arguments) and assigns color (to demonstrate DOM
modification). modification).
@example <doc:example>
<doc:source>
<script type="text/javascript"> <script type="text/javascript">
angular.filter('reverse', function(input, uppercase, color) { angular.filter('reverse', function(input, uppercase, color) {
var out = ""; var out = "";
@ -73,4 +74,14 @@ You can use these variables in the function:
Reverse: {{text|reverse}}<br> Reverse: {{text|reverse}}<br>
Reverse + uppercase: {{text|reverse:true}}<br> Reverse + uppercase: {{text|reverse:true}}<br>
Reverse + uppercase + blue: {{text|reverse:true:"blue"}} Reverse + uppercase + blue: {{text|reverse:true:"blue"}}
</doc:source>
<doc:scenario>
it('should reverse text', function(){
expect(binding('text|reverse')).toEqual('olleh');
input('text').enter('ABC');
expect(binding('text|reverse')).toEqual('CBA');
});
</doc:scenario>
</doc:example>

View file

@ -37,6 +37,8 @@ angular.formatter('reverse', {
</pre> </pre>
@example @example
<doc:example>
<doc:source>
<script type="text/javascript"> <script type="text/javascript">
function reverse(text) { function reverse(text) {
var reversed = []; var reversed = [];
@ -63,9 +65,8 @@ Formatted:
Stored: Stored:
<input type="text" name="data"/><br/> <input type="text" name="data"/><br/>
<pre>{{data}}</pre> <pre>{{data}}</pre>
</doc:source>
<doc:scenario>
@scenario
it('should store reverse', function(){ it('should store reverse', function(){
expect(element('.doc-example input:first').val()).toEqual('angular'); expect(element('.doc-example input:first').val()).toEqual('angular');
expect(element('.doc-example input:last').val()).toEqual('RALUGNA'); expect(element('.doc-example input:last').val()).toEqual('RALUGNA');
@ -76,3 +77,6 @@ it('should store reverse', function(){
}); });
expect(element('.doc-example input:first').val()).toEqual('zyx'); expect(element('.doc-example input:first').val()).toEqual('zyx');
}); });
</doc:scenario>
</doc:example>

View file

@ -17,22 +17,22 @@ services if needed.
Like other core angular variables and identifiers, the built-in services always start with `$`. Like other core angular variables and identifiers, the built-in services always start with `$`.
* `{@link angular.service.$browser $browser}` * {@link angular.service.$browser $browser}
* `{@link angular.service.$window $window}` * {@link angular.service.$window $window}
* `{@link angular.service.$document $document}` * {@link angular.service.$document $document}
* `{@link angular.service.$location $location}` * {@link angular.service.$location $location}
* `{@link angular.service.$log $log}` * {@link angular.service.$log $log}
* `{@link angular.service.$exceptionHandler $exceptionHandler}` * {@link angular.service.$exceptionHandler $exceptionHandler}
* `{@link angular.service.$hover $hover}` * {@link angular.service.$hover $hover}
* `{@link angular.service.$invalidWidgets $invalidWidgets}` * {@link angular.service.$invalidWidgets $invalidWidgets}
* `{@link angular.service.$route $route}` * {@link angular.service.$route $route}
* `{@link angular.service.$xhr $xhr}` * {@link angular.service.$xhr $xhr}
* `{@link angular.service.$xhr.error $xhr.error}` * {@link angular.service.$xhr.error $xhr.error}
* `{@link angular.service.$xhr.bulk $xhr.bulk}` * {@link angular.service.$xhr.bulk $xhr.bulk}
* `{@link angular.service.$xhr.cache $xhr.cache}` * {@link angular.service.$xhr.cache $xhr.cache}
* `{@link angular.service.$resource $resource}` * {@link angular.service.$resource $resource}
* `{@link angular.service.$cookies $cookies}` * {@link angular.service.$cookies $cookies}
* `{@link angular.service.$cookieStore $cookieStore}` * {@link angular.service.$cookieStore $cookieStore}
# Writing your own custom services # Writing your own custom services
angular provides only set of basic services, so for any nontrivial application it will be necessary angular provides only set of basic services, so for any nontrivial application it will be necessary
@ -138,6 +138,8 @@ myController.$inject = ['$location', '$log'];
</pre> </pre>
@example @example
<doc:example>
<doc:source>
<script type="text/javascript"> <script type="text/javascript">
angular.service('notify', function(win) { angular.service('notify', function(win) {
var msgs = []; var msgs = [];
@ -164,3 +166,10 @@ myController.$inject = ['$location', '$log'];
<input ng:init="message='test'" type="text" name="message" /> <input ng:init="message='test'" type="text" name="message" />
<button ng:click="callNotify(message);">NOTIFY</button> <button ng:click="callNotify(message);">NOTIFY</button>
</div> </div>
</doc:source>
<doc:scenario>
it('should test service', function(){
expect(element(':input[name=message]').val()).toEqual('test');
});
</doc:scenario>
</doc:example>

View file

@ -50,6 +50,8 @@ UPS tracking number.
default. default.
@example @example
<doc:example>
<doc:source>
<script> <script>
angular.validator('upsTrackingNo', function(input, format) { angular.validator('upsTrackingNo', function(input, format) {
var regexp = new RegExp("^" + format.replace(/9/g, '\\d') + "$"); var regexp = new RegExp("^" + format.replace(/9/g, '\\d') + "$");
@ -59,8 +61,8 @@ UPS tracking number.
<input type="text" name="trackNo" size="40" <input type="text" name="trackNo" size="40"
ng:validate="upsTrackingNo:'1Z 999 999 99 9999 999 9'" ng:validate="upsTrackingNo:'1Z 999 999 99 9999 999 9'"
value="1Z 123 456 78 9012 345 6"/> value="1Z 123 456 78 9012 345 6"/>
</doc:source>
@scenario <doc:scenario>
it('should validate correct UPS tracking number', function() { it('should validate correct UPS tracking number', function() {
expect(element('input[name=trackNo]').attr('class')). expect(element('input[name=trackNo]').attr('class')).
not().toMatch(/ng-validation-error/); not().toMatch(/ng-validation-error/);
@ -71,3 +73,5 @@ it('should not validate in correct UPS tracking number', function() {
expect(element('input[name=trackNo]').attr('class')). expect(element('input[name=trackNo]').attr('class')).
toMatch(/ng-validation-error/); toMatch(/ng-validation-error/);
}); });
</doc:scenario>
</doc:example>

View file

@ -57,6 +57,8 @@ angular.widget('@my:watch', function(expression, compileElement) {
</pre> </pre>
@example @example
<doc:example>
<doc:source>
<script> <script>
angular.widget('my:time', function(compileElement){ angular.widget('my:time', function(compileElement){
compileElement.css('display', 'block'); compileElement.css('display', 'block');
@ -70,4 +72,7 @@ angular.widget('@my:watch', function(expression, compileElement) {
}); });
</script> </script>
<my:time></my:time> <my:time></my:time>
</doc:source>
<doc:scenario>
</doc:scenario>
</doc:example>

View file

@ -7,34 +7,6 @@ describe('dom', function(){
dom = new DOM(); dom = new DOM();
}); });
describe('example', function(){
it('should render code, live, test', function(){
dom.example('desc', 'src', 'scenario');
expect(dom.toString()).toEqual(
'<h1>Example</h1>\n' +
'<div class="example">' +
'desc<doc:example><doc:source>src</doc:source>\n' +
'<doc:scenario>scenario</doc:scenario>\n'+
'</doc:example>\n' +
'</div>\n');
});
it('should render non-live, test with description', function(){
dom.example('desc', 'src', false);
expect(dom.toString()).toEqual('<h1>Example</h1>\n' +
'<div class="example">' +
'desc<div ng:non-bindable="">' +
'<pre class="brush: js; html-script: true;">src</pre>\n' +
'</div>\n' +
'</div>\n');
});
it('should render non-live, test', function(){
dom.example('desc', 'src', false);
expect(dom.toString()).toContain('<pre class="brush: js; html-script: true;">src</pre>');
});
});
describe('h', function(){ describe('h', function(){
it('should render using function', function(){ it('should render using function', function(){

View file

@ -74,12 +74,6 @@ describe('ngdoc', function(){
}); });
}); });
it('should not remove extra line breaks', function(){
var doc = new Doc('@example\nA\n\nB');
doc.parse();
expect(doc.example).toEqual('A\n\nB');
});
it('should parse filename', function(){ it('should parse filename', function(){
var doc = new Doc('@name friendly name', 'docs/a.b.ngdoc', 1); var doc = new Doc('@name friendly name', 'docs/a.b.ngdoc', 1);
doc.parse(0); doc.parse(0);
@ -128,32 +122,14 @@ describe('ngdoc', function(){
}); });
}); });
describe('scenario', function(){
it('should render from @example/@scenario and <doc:example>', function(){
var doc = new Doc(
'@id id\n' +
'@description <doc:example><doc:scenario>scenario0</doc:scenario></doc:example>' +
'@example exempleText\n' +
'@scenario scenario1\n' +
'@scenario scenario2').parse();
expect(ngdoc.scenarios([doc])).toContain('describe("id"');
expect(ngdoc.scenarios([doc])).toContain('navigateTo("index.html#!id")');
expect(ngdoc.scenarios([doc])).toContain('\n scenario0\n');
expect(ngdoc.scenarios([doc])).toContain('\n scenario1\n');
expect(ngdoc.scenarios([doc])).toContain('\n scenario2\n');
});
});
describe('markdown', function(){ describe('markdown', function(){
var markdown = ngdoc.markdown;
it('should replace angular in markdown', function(){ it('should replace angular in markdown', function(){
expect(markdown('<angular/>')). expect(new Doc().markdown('<angular/>')).
toEqual('<p><tt>&lt;angular/&gt;</tt></p>'); toEqual('<p><tt>&lt;angular/&gt;</tt></p>');
}); });
it('should not replace anything in <pre>, but escape the html escape the content', function(){ it('should not replace anything in <pre>, but escape the html escape the content', function(){
expect(markdown('bah x\n<pre>\n<b>angular</b>.k\n</pre>\n asdf x')). expect(new Doc().markdown('bah x\n<pre>\n<b>angular</b>.k\n</pre>\n asdf x')).
toEqual( toEqual(
'<p>bah x</p>' + '<p>bah x</p>' +
'<div ng:non-bindable><pre class="brush: js; html-script: true;">\n' + '<div ng:non-bindable><pre class="brush: js; html-script: true;">\n' +
@ -163,7 +139,7 @@ describe('ngdoc', function(){
}); });
it('should replace text between two <pre></pre> tags', function() { it('should replace text between two <pre></pre> tags', function() {
expect(markdown('<pre>x</pre># One<pre>b</pre>')). expect(new Doc().markdown('<pre>x</pre># One<pre>b</pre>')).
toMatch('</div><h1>One</h1><div'); toMatch('</div><h1>One</h1><div');
}); });
@ -340,38 +316,20 @@ describe('ngdoc', function(){
it('should not remove {{}}', function(){ it('should not remove {{}}', function(){
var doc = new Doc('@example text {{ abc }}'); var doc = new Doc('@example text {{ abc }}');
doc.parse(); doc.parse();
expect(doc.example).toEqual('text {{ abc }}'); expect(doc.example).toEqual('<p>text {{ abc }}</p>');
});
}); });
describe('@exampleDescription', function(){ it('should support doc:example', function(){
it('should render example description', function(){ var doc = new Doc('@ngdoc overview\n@example \n' +
var doc = new Doc('@exampleDescription some\n text'); '<doc:example>\n' +
doc.ngdoc = "filter"; ' <doc:source><escapeme></doc:source>\n' +
doc.parse(); ' <doc:scenario><scenario></doc:scenario>\n' +
expect(doc.html()).toContain('<p>some\n text'); '</doc:example>').parse();
var html = doc.html();
expect(html).toContain('<doc:source>&lt;escapeme&gt;</doc:source>');
expect(html).toContain('<doc:scenario>&lt;scenario&gt;</doc:scenario>');
expect(doc.scenarios).toEqual(['<scenario>']);
}); });
it('should alias @exampleDescription to @exampleDesc', function(){
var doc = new Doc('@exampleDesc some\n text');
doc.ngdoc = "filter";
doc.parse();
expect(doc.html()).toContain('<p>some\n text');
});
it('should render description in related method', function(){
var doc = new Doc('').parse();
doc.ngdoc = 'service';
doc.methods = [
new Doc('@ngdoc method\n@exampleDescription MDesc\n@example MExmp').parse()];
doc.properties = [
new Doc('@ngdoc property\n@exampleDescription PDesc\n@example PExmp').parse()];
expect(doc.html()).toContain('<p>MDesc</p><div ng:non-bindable="">' +
'<pre class="brush: js; html-script: true;">MExmp</pre>');
expect(doc.html()).toContain('<p>PDesc</p><div ng:non-bindable="">' +
'<pre class="brush: js; html-script: true;">PExmp</pre>');
});
}); });
describe('@depricated', function() { describe('@depricated', function() {

View file

@ -74,25 +74,8 @@ DOM.prototype = {
}); });
}, },
example: function(description, source, scenario) {
if (description || source || scenario) {
this.h('Example', function(){
if (description)
this.html(description);
if (scenario === false) {
this.code(source);
} else {
this.tag('doc:example', function(){
if (source) this.tag('doc:source', source);
if (scenario) this.tag('doc:scenario', scenario);
});
}
});
}
},
h: function(heading, content, fn){ h: function(heading, content, fn){
if (content==undefined || content && content.legth == 0) return; if (content==undefined || (content instanceof Array && content.length == 0)) return;
this.headingDepth++; this.headingDepth++;
this.tag('h' + this.headingDepth, heading); this.tag('h' + this.headingDepth, heading);
var className = typeof heading == 'string' var className = typeof heading == 'string'

View file

@ -7,7 +7,6 @@ var DOM = require('dom.js').DOM;
var htmlEscape = require('dom.js').htmlEscape; var htmlEscape = require('dom.js').htmlEscape;
var NEW_LINE = /\n\r?/; var NEW_LINE = /\n\r?/;
exports.markdown = markdown;
exports.trim = trim; exports.trim = trim;
exports.metadata = metadata; exports.metadata = metadata;
exports.scenarios = scenarios; exports.scenarios = scenarios;
@ -25,6 +24,11 @@ function Doc(text, file, line) {
this.file = file; this.file = file;
this.line = line; this.line = line;
} }
this.scenarios = this.scenarios || [];
this.requires = this.requires || [];
this.param = this.param || [];
this.properties = this.properties || [];
this.methods = this.methods || [];
} }
Doc.METADATA_IGNORE = (function(){ Doc.METADATA_IGNORE = (function(){
var words = require('fs').readFileSync(__dirname + '/ignore.words', 'utf8'); var words = require('fs').readFileSync(__dirname + '/ignore.words', 'utf8');
@ -53,16 +57,53 @@ Doc.prototype = {
return words.join(' '); return words.join(' ');
}, },
markdown: function (text) {
var self = this;
var IS_URL = /^(https?:\/\/|ftps?:\/\/|mailto:)/;
var IS_ANGULAR = /^angular\./;
if (!text) return text;
var parts = text.split(/(<pre>[\s\S]*?<\/pre>|<doc:example>[\s\S]*?<\/doc:example>)/),
match;
parts.forEach(function(text, i){
if (text.match(/^<pre>/)) {
text = text.replace(/^<pre>([\s\S]*)<\/pre>/mi, function(_, content){
return '<div ng:non-bindable><pre class="brush: js; html-script: true;">' +
content.replace(/</g, '&lt;').replace(/>/g, '&gt;') +
'</pre></div>';
});
} else if (text.match(/^<doc:example>/)) {
text = text.replace(/(<doc:source>)([\s\S]*)(<\/doc:source>)/mi,
function(_, before, content, after){
return before + htmlEscape(content) + after;
});
text = text.replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi,
function(_, before, content, after){
self.scenarios.push(content);
return before + htmlEscape(content) + after;
});
} else {
text = text.replace(/<angular\/>/gm, '<tt>&lt;angular/&gt;</tt>');
text = text.replace(/{@link ([^\s}]+)((\s|\n)+(.+?))?\s*}/gm,
function(_all, url, _2, _3, title){
return '<a href="' + (url.match(IS_URL) ? '' : '#!') + url + '">'
+ (url.match(IS_ANGULAR) ? '<code>' : '')
+ (title || url)
+ (url.match(IS_ANGULAR) ? '</code>' : '')
+ '</a>';
});
text = new Showdown.converter().makeHtml(text);
}
parts[i] = text;
});
return parts.join('');
},
parse: function(){ parse: function(){
var atName; var atName;
var atText; var atText;
var match; var match;
var self = this; var self = this;
this.scenarios = [];
this.requires = [];
this.param = [];
this.properties = [];
this.methods = [];
self.text.split(NEW_LINE).forEach(function(line){ self.text.split(NEW_LINE).forEach(function(line){
if (match = line.match(/^\s*@(\w+)(\s+(.*))?/)) { if (match = line.match(/^\s*@(\w+)(\s+(.*))?/)) {
// we found @name ... // we found @name ...
@ -82,9 +123,9 @@ Doc.prototype = {
this.id = this.id // if we have an id just use it this.id = this.id // if we have an id just use it
|| (((this.file||'').match(/.*\/([^\/]*)\.ngdoc/)||{})[1]) // try to extract it from file name || (((this.file||'').match(/.*\/([^\/]*)\.ngdoc/)||{})[1]) // try to extract it from file name
|| this.name; // default to name || this.name; // default to name
this.description = markdown(this.description); this.description = this.markdown(this.description);
this['this'] = markdown(this['this']); this.example = this.markdown(this.example);
this.exampleDescription = markdown(this.exampleDescription || this.exampleDesc); this['this'] = this.markdown(this['this']);
return this; return this;
function flush(){ function flush(){
@ -98,7 +139,7 @@ Doc.prototype = {
} }
var param = { var param = {
name: match[5] || match[4], name: match[5] || match[4],
description:markdown(text.replace(match[0], match[7])), description:self.markdown(text.replace(match[0], match[7])),
type: match[1], type: match[1],
optional: !!match[2], optional: !!match[2],
'default':match[6] 'default':match[6]
@ -111,18 +152,10 @@ Doc.prototype = {
} }
self.returns = { self.returns = {
type: match[1], type: match[1],
description: markdown(text.replace(match[0], match[2])) description: self.markdown(text.replace(match[0], match[2]))
}; };
} else if(atName == 'description') {
text.replace(/<doc:scenario>([\s\S]*?)<\/doc:scenario>/gmi,
function(_, scenario){
self.scenarios.push(scenario);
});
self.description = text;
} else if(atName == 'requires') { } else if(atName == 'requires') {
self.requires.push(text); self.requires.push(text);
} else if(atName == 'scenario') {
self.scenarios.push(text);
} else if(atName == 'property') { } else if(atName == 'property') {
var match = text.match(/^({(\S+)}\s*)?(\S+)(\s+(.*))?/); var match = text.match(/^({(\S+)}\s*)?(\S+)(\s+(.*))?/);
if (!match) { if (!match) {
@ -154,7 +187,7 @@ Doc.prototype = {
throw new Error("Don't know how to format @ngdoc: " + self.ngdoc); throw new Error("Don't know how to format @ngdoc: " + self.ngdoc);
}).call(self, dom); }).call(self, dom);
dom.example(self.exampleDescription, self.example, self.scenarios[0]); dom.h('Example', self.example, dom.html);
}); });
return dom.toString(); return dom.toString();
@ -407,13 +440,13 @@ Doc.prototype = {
dom.h(method.shortName + '(' + signature.join(', ') + ')', method, function(){ dom.h(method.shortName + '(' + signature.join(', ') + ')', method, function(){
dom.html(method.description); dom.html(method.description);
method.html_usage_parameters(dom); method.html_usage_parameters(dom);
dom.example(method.exampleDescription, method.example, false); dom.h('Example', method.example, dom.html);
}); });
}); });
dom.h('Properties', this.properties, function(property){ dom.h('Properties', this.properties, function(property){
dom.h(property.name, function(){ dom.h(property.name, function(){
dom.text(property.description); dom.text(property.description);
dom.example(property.exampleDescription, property.example, false); dom.h('Example', property.example, dom.html);
}); });
}); });
}, },
@ -436,47 +469,6 @@ Doc.prototype = {
////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////
function markdown (text) {
var IS_URL = /^(https?:\/\/|ftps?:\/\/|mailto:)/;
var IS_ANGULAR = /^angular\./;
if (!text) return text;
var parts = text.split(/(<pre>[\s\S]*?<\/pre>|<doc:example>[\s\S]*?<\/doc:example>)/),
match;
parts.forEach(function(text, i){
if (text.match(/^<pre>/)) {
text = text.replace(/^<pre>([\s\S]*)<\/pre>/mi, function(_, content){
return '<div ng:non-bindable><pre class="brush: js; html-script: true;">' +
content.replace(/</g, '&lt;').replace(/>/g, '&gt;') +
'</pre></div>';
});
} else if (text.match(/^<doc:example>/)) {
text = text.replace(/(<doc:source>)([\s\S]*)(<\/doc:source>)/mi,
function(_, before, content, after){
return before + htmlEscape(content) + after;
});
text = text.replace(/(<doc:scenario>)([\s\S]*)(<\/doc:scenario>)/mi,
function(_, before, content, after){
return before + htmlEscape(content) + after;
});
} else {
text = text.replace(/<angular\/>/gm, '<tt>&lt;angular/&gt;</tt>');
text = text.replace(/{@link ([^\s}]+)((\s|\n)+(.+?))?\s*}/gm,
function(_all, url, _2, _3, title){
return '<a href="' + (url.match(IS_URL) ? '' : '#!') + url + '">'
+ (url.match(IS_ANGULAR) ? '<code>' : '')
+ (title || url)
+ (url.match(IS_ANGULAR) ? '</code>' : '')
+ '</a>';
});
text = new Showdown.converter().makeHtml(text);
}
parts[i] = text;
});
return parts.join('');
};
////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////
function scenarios(docs){ function scenarios(docs){
var specs = []; var specs = [];

View file

@ -484,14 +484,18 @@ function map(obj, iterator, context) {
* @returns {number} The size of `obj` or `0` if `obj` is neither an object or an array. * @returns {number} The size of `obj` or `0` if `obj` is neither an object or an array.
* *
* @example * @example
* <doc:example>
* <doc:source>
* Number of items in array: {{ [1,2].$size() }}<br/> * Number of items in array: {{ [1,2].$size() }}<br/>
* Number of items in object: {{ {a:1, b:2, c:3}.$size() }}<br/> * Number of items in object: {{ {a:1, b:2, c:3}.$size() }}<br/>
* * </doc:source>
* @scenario * <doc:scenario>
it('should print correct sizes for an array and an object', function() { * it('should print correct sizes for an array and an object', function() {
expect(binding('[1,2].$size()')).toBe('2'); * expect(binding('[1,2].$size()')).toBe('2');
expect(binding('{a:1, b:2, c:3}.$size()')).toBe('3'); * expect(binding('{a:1, b:2, c:3}.$size()')).toBe('3');
}); * });
* </doc:scenario>
* </doc:example>
*/ */
function size(obj) { function size(obj) {
var size = 0, key; var size = 0, key;
@ -556,6 +560,8 @@ function isLeafNode (node) {
* @returns {*} The copy or updated `destination` if `destination` was specified. * @returns {*} The copy or updated `destination` if `destination` was specified.
* *
* @example * @example
* <doc:example>
* <doc:source>
Salutation: <input type="text" name="master.salutation" value="Hello" /><br/> Salutation: <input type="text" name="master.salutation" value="Hello" /><br/>
Name: <input type="text" name="master.name" value="world"/><br/> Name: <input type="text" name="master.name" value="world"/><br/>
<button ng:click="form = master.$copy()">copy</button> <button ng:click="form = master.$copy()">copy</button>
@ -565,8 +571,8 @@ function isLeafNode (node) {
<pre>master={{master}}</pre> <pre>master={{master}}</pre>
<pre>form={{form}}</pre> <pre>form={{form}}</pre>
* </doc:source>
* @scenario * <doc:scenario>
it('should print that initialy the form object is NOT equal to master', function() { it('should print that initialy the form object is NOT equal to master', function() {
expect(element('.doc-example input[name=master.salutation]').val()).toBe('Hello'); expect(element('.doc-example input[name=master.salutation]').val()).toBe('Hello');
expect(element('.doc-example input[name=master.name]').val()).toBe('world'); expect(element('.doc-example input[name=master.name]').val()).toBe('world');
@ -577,6 +583,8 @@ function isLeafNode (node) {
element('.doc-example button').click(); element('.doc-example button').click();
expect(element('.doc-example span').css('display')).toBe('none'); expect(element('.doc-example span').css('display')).toBe('none');
}); });
* </doc:scenario>
* </doc:example>
*/ */
function copy(source, destination){ function copy(source, destination){
if (!destination) { if (!destination) {
@ -633,6 +641,8 @@ function copy(source, destination){
* @returns {boolean} True if arguments are equal. * @returns {boolean} True if arguments are equal.
* *
* @example * @example
* <doc:example>
* <doc:source>
Salutation: <input type="text" name="greeting.salutation" value="Hello" /><br/> Salutation: <input type="text" name="greeting.salutation" value="Hello" /><br/>
Name: <input type="text" name="greeting.name" value="world"/><br/> Name: <input type="text" name="greeting.name" value="world"/><br/>
<hr/> <hr/>
@ -642,8 +652,8 @@ function copy(source, destination){
<code>{salutation:'Hello', name:'world'}</code>. <code>{salutation:'Hello', name:'world'}</code>.
<pre>greeting={{greeting}}</pre> <pre>greeting={{greeting}}</pre>
* </doc:source>
@scenario * <doc:scenario>
it('should print that initialy greeting is equal to the hardcoded value object', function() { it('should print that initialy greeting is equal to the hardcoded value object', function() {
expect(element('.doc-example input[name=greeting.salutation]').val()).toBe('Hello'); expect(element('.doc-example input[name=greeting.salutation]').val()).toBe('Hello');
expect(element('.doc-example input[name=greeting.name]').val()).toBe('world'); expect(element('.doc-example input[name=greeting.name]').val()).toBe('world');
@ -654,6 +664,8 @@ function copy(source, destination){
input('greeting.name').enter('kitty'); input('greeting.name').enter('kitty');
expect(element('.doc-example span').css('display')).toBe('inline'); expect(element('.doc-example span').css('display')).toBe('inline');
}); });
* </doc:scenario>
* </doc:example>
*/ */
function equals(o1, o2) { function equals(o1, o2) {
if (o1 == o2) return true; if (o1 == o2) return true;

View file

@ -139,9 +139,11 @@ Compiler.prototype = {
* @element ANY * @element ANY
* @param {integer|string=} [priority=0] priority integer, or FIRST, LAST constant * @param {integer|string=} [priority=0] priority integer, or FIRST, LAST constant
* *
* @exampleDescription * @example
* try changing the invoice and see that the Total will lag in evaluation * try changing the invoice and see that the Total will lag in evaluation
* @example * @example
<doc:example>
<doc:source>
<div>TOTAL: without ng:eval-order {{ items.$sum('total') | currency }}</div> <div>TOTAL: without ng:eval-order {{ items.$sum('total') | currency }}</div>
<div ng:eval-order='LAST'>TOTAL: with ng:eval-order {{ items.$sum('total') | currency }}</div> <div ng:eval-order='LAST'>TOTAL: with ng:eval-order {{ items.$sum('total') | currency }}</div>
<table ng:init="items=[{qty:1, cost:9.99, desc:'gadget'}]"> <table ng:init="items=[{qty:1, cost:9.99, desc:'gadget'}]">
@ -164,8 +166,8 @@ Compiler.prototype = {
<td>{{ items.$sum('total') | currency }}</td> <td>{{ items.$sum('total') | currency }}</td>
</tr> </tr>
</table> </table>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:format', function(){ it('should check ng:format', function(){
expect(using('.doc-example-live div:first').binding("items.$sum('total')")).toBe('$9.99'); expect(using('.doc-example-live div:first').binding("items.$sum('total')")).toBe('$9.99');
expect(using('.doc-example-live div:last').binding("items.$sum('total')")).toBe('$9.99'); expect(using('.doc-example-live div:last').binding("items.$sum('total')")).toBe('$9.99');
@ -173,6 +175,8 @@ Compiler.prototype = {
expect(using('.doc-example-live div:first').binding("items.$sum('total')")).toBe('$9.99'); expect(using('.doc-example-live div:first').binding("items.$sum('total')")).toBe('$9.99');
expect(using('.doc-example-live div:last').binding("items.$sum('total')")).toBe('$19.98'); expect(using('.doc-example-live div:last').binding("items.$sum('total')")).toBe('$19.98');
}); });
</doc:scenario>
</doc:example>
*/ */
templatize: function(element, elementIndex, priority){ templatize: function(element, elementIndex, priority){

View file

@ -202,7 +202,7 @@ function errorHandlerFor(element, error) {
* @returns {Object} Newly created scope. * @returns {Object} Newly created scope.
* *
* *
* @exampleDescription * @example
* This example demonstrates scope inheritance and property overriding. * This example demonstrates scope inheritance and property overriding.
* *
* In this example, the root scope encompasses the whole HTML DOM tree. This scope has `salutation`, * In this example, the root scope encompasses the whole HTML DOM tree. This scope has `salutation`,
@ -216,7 +216,8 @@ function errorHandlerFor(element, error) {
* - The child scope inherits the salutation property from the root scope. * - The child scope inherits the salutation property from the root scope.
* - The $index property does not leak from the child scope to the root scope. * - The $index property does not leak from the child scope to the root scope.
* *
* @example <doc:example>
<doc:source>
<ul ng:init="salutation='Hello'; name='Misko'; names=['World', 'Earth']"> <ul ng:init="salutation='Hello'; name='Misko'; names=['World', 'Earth']">
<li ng:repeat="name in names"> <li ng:repeat="name in names">
{{$index}}: {{salutation}} {{name}}! {{$index}}: {{salutation}} {{name}}!
@ -226,8 +227,8 @@ function errorHandlerFor(element, error) {
$index={{$index}} $index={{$index}}
salutation={{salutation}} salutation={{salutation}}
name={{name}}</pre> name={{name}}</pre>
</doc:source>
@scenario <doc:scenario>
it('should inherit the salutation property and override the name property', function() { it('should inherit the salutation property and override the name property', function() {
expect(using('.doc-example-live').repeater('li').row(0)). expect(using('.doc-example-live').repeater('li').row(0)).
toEqual(['0', 'Hello', 'World']); toEqual(['0', 'Hello', 'World']);
@ -236,7 +237,8 @@ function errorHandlerFor(element, error) {
expect(using('.doc-example-live').element('pre').text()). expect(using('.doc-example-live').element('pre').text()).
toBe('$index=\nsalutation=Hello\nname=Misko'); toBe('$index=\nsalutation=Hello\nname=Misko');
}); });
</doc:scenario>
</doc:example>
*/ */
function createScope(parent, providers, instanceCache) { function createScope(parent, providers, instanceCache) {
function Parent(){} function Parent(){}

28
src/angular-mocks.js vendored
View file

@ -239,10 +239,10 @@ angular.service('$exceptionHandler', function(e) {
*/ */
angular.service('$log', function() { angular.service('$log', function() {
var $log = { var $log = {
log: function(){ $log.logs.push(arguments) }, log: function(){ $log.logs.push(arguments); },
warn: function(){ $log.logs.push(arguments) }, warn: function(){ $log.logs.push(arguments); },
info: function(){ $log.logs.push(arguments) }, info: function(){ $log.logs.push(arguments); },
error: function(){ $log.logs.push(arguments) } error: function(){ $log.logs.push(arguments); }
}; };
$log.log.logs = []; $log.log.logs = [];
@ -265,15 +265,6 @@ angular.service('$log', function() {
* @param {(number|string)} timestamp Timestamp representing the desired time in *UTC* * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC*
* *
* @example * @example
* var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
* newYearInBratislava.getTimezoneOffset() => -60;
* newYearInBratislava.getFullYear() => 2010;
* newYearInBratislava.getMonth() => 0;
* newYearInBratislava.getDate() => 1;
* newYearInBratislava.getHours() => 0;
* newYearInBratislava.getMinutes() => 0;
*
*
* !!!! WARNING !!!!! * !!!! WARNING !!!!!
* This is not a complete Date object so only methods that were implemented can be called safely. * This is not a complete Date object so only methods that were implemented can be called safely.
* To make matters worse, TzDate instances inherit stuff from Date via a prototype. * To make matters worse, TzDate instances inherit stuff from Date via a prototype.
@ -281,6 +272,17 @@ angular.service('$log', function() {
* We do our best to intercept calls to "unimplemented" methods, but since the list of methods is * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is
* incomplete we might be missing some non-standard methods. This can result in errors like: * incomplete we might be missing some non-standard methods. This can result in errors like:
* "Date.prototype.foo called on incompatible Object". * "Date.prototype.foo called on incompatible Object".
*
* <pre>
* var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
* newYearInBratislava.getTimezoneOffset() => -60;
* newYearInBratislava.getFullYear() => 2010;
* newYearInBratislava.getMonth() => 0;
* newYearInBratislava.getDate() => 1;
* newYearInBratislava.getHours() => 0;
* newYearInBratislava.getMinutes() => 0;
* </pre>
*
*/ */
function TzDate(offset, timestamp) { function TzDate(offset, timestamp) {
if (angular.isString(timestamp)) { if (angular.isString(timestamp)) {

View file

@ -79,11 +79,13 @@ var angularArray = {
* @returns {number} The position of the element in `array`. The position is 0-based. `-1` is returned if the value can't be found. * @returns {number} The position of the element in `array`. The position is 0-based. `-1` is returned if the value can't be found.
* *
* @example * @example
<doc:example>
<doc:source>
<div ng:init="books = ['Moby Dick', 'Great Gatsby', 'Romeo and Juliet']"></div> <div ng:init="books = ['Moby Dick', 'Great Gatsby', 'Romeo and Juliet']"></div>
<input name='bookName' value='Romeo and Juliet'> <br> <input name='bookName' value='Romeo and Juliet'> <br>
Index of '{{bookName}}' in the list {{books}} is <em>{{books.$indexOf(bookName)}}</em>. Index of '{{bookName}}' in the list {{books}} is <em>{{books.$indexOf(bookName)}}</em>.
</doc:source>
@scenario <doc:scenario>
it('should correctly calculate the initial index', function() { it('should correctly calculate the initial index', function() {
expect(binding('books.$indexOf(bookName)')).toBe('2'); expect(binding('books.$indexOf(bookName)')).toBe('2');
}); });
@ -95,6 +97,8 @@ var angularArray = {
input('bookName').enter('Moby Dick'); input('bookName').enter('Moby Dick');
expect(binding('books.$indexOf(bookName)')).toBe('0'); expect(binding('books.$indexOf(bookName)')).toBe('0');
}); });
</doc:scenario>
</doc:example>
*/ */
'indexOf': indexOf, 'indexOf': indexOf,
@ -117,6 +121,8 @@ var angularArray = {
* @returns {number} Sum of items in the array. * @returns {number} Sum of items in the array.
* *
* @example * @example
<doc:example>
<doc:source>
<table ng:init="invoice= {items:[{qty:10, description:'gadget', cost:9.95}]}"> <table ng:init="invoice= {items:[{qty:10, description:'gadget', cost:9.95}]}">
<tr><th>Qty</th><th>Description</th><th>Cost</th><th>Total</th><th></th></tr> <tr><th>Qty</th><th>Description</th><th>Cost</th><th>Total</th><th></th></tr>
<tr ng:repeat="item in invoice.items"> <tr ng:repeat="item in invoice.items">
@ -133,8 +139,8 @@ var angularArray = {
<td>{{invoice.items.$sum('qty*cost') | currency}}</td> <td>{{invoice.items.$sum('qty*cost') | currency}}</td>
</tr> </tr>
</table> </table>
</doc:source>
@scenario <doc:scenario>
//TODO: these specs are lame because I had to work around issues #164 and #167 //TODO: these specs are lame because I had to work around issues #164 and #167
it('should initialize and calculate the totals', function() { it('should initialize and calculate the totals', function() {
expect(repeater('.doc-example-live table tr', 'item in invoice.items').count()).toBe(3); expect(repeater('.doc-example-live table tr', 'item in invoice.items').count()).toBe(3);
@ -153,6 +159,8 @@ var angularArray = {
toEqual(['$2,000.00']); toEqual(['$2,000.00']);
expect(binding("invoice.items.$sum('qty*cost')")).toBe('$2,099.50'); expect(binding("invoice.items.$sum('qty*cost')")).toBe('$2,099.50');
}); });
</doc:scenario>
</doc:example>
*/ */
'sum':function(array, expression) { 'sum':function(array, expression) {
var fn = angular['Function']['compile'](expression); var fn = angular['Function']['compile'](expression);
@ -185,6 +193,8 @@ var angularArray = {
* @returns {*} The removed element. * @returns {*} The removed element.
* *
* @example * @example
<doc:example>
<doc:source>
<ul ng:init="tasks=['Learn Angular', 'Read Documentation', <ul ng:init="tasks=['Learn Angular', 'Read Documentation',
'Check out demos', 'Build cool applications']"> 'Check out demos', 'Build cool applications']">
<li ng:repeat="task in tasks"> <li ng:repeat="task in tasks">
@ -193,8 +203,8 @@ var angularArray = {
</ul> </ul>
<hr/> <hr/>
tasks = {{tasks}} tasks = {{tasks}}
</doc:source>
@scenario <doc:scenario>
it('should initialize the task list with for tasks', function() { it('should initialize the task list with for tasks', function() {
expect(repeater('.doc-example ul li', 'task in tasks').count()).toBe(4); expect(repeater('.doc-example ul li', 'task in tasks').count()).toBe(4);
expect(repeater('.doc-example ul li', 'task in tasks').column('task')). expect(repeater('.doc-example ul li', 'task in tasks').column('task')).
@ -212,6 +222,8 @@ var angularArray = {
expect(repeater('.doc-example ul li', 'task in tasks').column('task')). expect(repeater('.doc-example ul li', 'task in tasks').column('task')).
toEqual(['Read Documentation', 'Check out demos']); toEqual(['Read Documentation', 'Check out demos']);
}); });
</doc:scenario>
</doc:example>
*/ */
'remove':function(array, value) { 'remove':function(array, value) {
var index = indexOf(array, value); var index = indexOf(array, value);
@ -254,6 +266,8 @@ var angularArray = {
* the predicate returned true for. * the predicate returned true for.
* *
* @example * @example
<doc:example>
<doc:source>
<div ng:init="friends = [{name:'John', phone:'555-1276'}, <div ng:init="friends = [{name:'John', phone:'555-1276'},
{name:'Mary', phone:'800-BIG-MARY'}, {name:'Mary', phone:'800-BIG-MARY'},
{name:'Mike', phone:'555-4321'}, {name:'Mike', phone:'555-4321'},
@ -279,8 +293,8 @@ var angularArray = {
<td>{{friend.phone}}</td> <td>{{friend.phone}}</td>
<tr> <tr>
</table> </table>
</doc:source>
@scenario <doc:scenario>
it('should search across all fields when filtering with a string', function() { it('should search across all fields when filtering with a string', function() {
input('searchText').enter('m'); input('searchText').enter('m');
expect(repeater('#searchTextResults tr', 'friend in friends').column('name')). expect(repeater('#searchTextResults tr', 'friend in friends').column('name')).
@ -296,6 +310,8 @@ var angularArray = {
expect(repeater('#searchObjResults tr', 'friend in friends').column('name')). expect(repeater('#searchObjResults tr', 'friend in friends').column('name')).
toEqual(['Mary', 'Mike', 'Julie']); toEqual(['Mary', 'Mike', 'Julie']);
}); });
</doc:scenario>
</doc:example>
*/ */
'filter':function(array, expression) { 'filter':function(array, expression) {
var predicates = []; var predicates = [];
@ -398,11 +414,11 @@ var angularArray = {
* *
* @TODO simplify the example. * @TODO simplify the example.
* *
* @exampleDescription * @example
* This example shows how an initially empty array can be filled with objects created from user * This example shows how an initially empty array can be filled with objects created from user
* input via the `$add` method. * input via the `$add` method.
* <doc:example>
* @example <doc:source>
[<a href="" ng:click="people.$add()">add empty</a>] [<a href="" ng:click="people.$add()">add empty</a>]
[<a href="" ng:click="people.$add({name:'John', sex:'male'})">add 'John'</a>] [<a href="" ng:click="people.$add({name:'John', sex:'male'})">add 'John'</a>]
[<a href="" ng:click="people.$add({name:'Mary', sex:'female'})">add 'Mary'</a>] [<a href="" ng:click="people.$add({name:'Mary', sex:'female'})">add 'Mary'</a>]
@ -419,8 +435,8 @@ var angularArray = {
</li> </li>
</ul> </ul>
<pre>people = {{people}}</pre> <pre>people = {{people}}</pre>
</doc:source>
@scenario <doc:scenario>
beforeEach(function() { beforeEach(function() {
expect(binding('people')).toBe('people = []'); expect(binding('people')).toBe('people = []');
}); });
@ -445,6 +461,8 @@ var angularArray = {
element('.doc-example li a:contains("X"):first').click(); element('.doc-example li a:contains("X"):first').click();
expect(binding('people')).toBe('people = []'); expect(binding('people')).toBe('people = []');
}); });
</doc:scenario>
</doc:example>
*/ */
'add':function(array, value) { 'add':function(array, value) {
array.push(isUndefined(value)? {} : value); array.push(isUndefined(value)? {} : value);
@ -471,6 +489,8 @@ var angularArray = {
* @returns {number} Number of elements in the array (for which the condition evaluates to true). * @returns {number} Number of elements in the array (for which the condition evaluates to true).
* *
* @example * @example
<doc:example>
<doc:source>
<pre ng:init="items = [{name:'knife', points:1}, <pre ng:init="items = [{name:'knife', points:1},
{name:'fork', points:3}, {name:'fork', points:3},
{name:'spoon', points:1}]"></pre> {name:'spoon', points:1}]"></pre>
@ -482,8 +502,8 @@ var angularArray = {
</ul> </ul>
<p>Number of items which have one point: <em>{{ items.$count('points==1') }}</em></p> <p>Number of items which have one point: <em>{{ items.$count('points==1') }}</em></p>
<p>Number of items which have more than one point: <em>{{items.$count('points&gt;1')}}</em></p> <p>Number of items which have more than one point: <em>{{items.$count('points&gt;1')}}</em></p>
</doc:source>
@scenario <doc:scenario>
it('should calculate counts', function() { it('should calculate counts', function() {
expect(binding('items.$count(\'points==1\')')).toEqual(2); expect(binding('items.$count(\'points==1\')')).toEqual(2);
expect(binding('items.$count(\'points>1\')')).toEqual(1); expect(binding('items.$count(\'points>1\')')).toEqual(1);
@ -494,6 +514,8 @@ var angularArray = {
expect(binding('items.$count(\'points==1\')')).toEqual(1); expect(binding('items.$count(\'points==1\')')).toEqual(1);
expect(binding('items.$count(\'points>1\')')).toEqual(2); expect(binding('items.$count(\'points>1\')')).toEqual(2);
}); });
</doc:scenario>
</doc:example>
*/ */
'count':function(array, condition) { 'count':function(array, condition) {
if (!condition) return array.length; if (!condition) return array.length;
@ -535,6 +557,8 @@ var angularArray = {
* @returns {Array} Sorted copy of the source array. * @returns {Array} Sorted copy of the source array.
* *
* @example * @example
<doc:example>
<doc:source>
<div ng:init="friends = [{name:'John', phone:'555-1212', age:10}, <div ng:init="friends = [{name:'John', phone:'555-1212', age:10},
{name:'Mary', phone:'555-9876', age:19}, {name:'Mary', phone:'555-9876', age:19},
{name:'Mike', phone:'555-4321', age:21}, {name:'Mike', phone:'555-4321', age:21},
@ -558,8 +582,8 @@ var angularArray = {
<td>{{friend.age}}</td> <td>{{friend.age}}</td>
<tr> <tr>
</table> </table>
</doc:source>
@scenario <doc:scenario>
it('should be reverse ordered by aged', function() { it('should be reverse ordered by aged', function() {
expect(binding('predicate')).toBe('Sorting predicate = -age'); expect(binding('predicate')).toBe('Sorting predicate = -age');
expect(repeater('.doc-example table', 'friend in friends').column('friend.age')). expect(repeater('.doc-example table', 'friend in friends').column('friend.age')).
@ -581,6 +605,8 @@ var angularArray = {
expect(repeater('.doc-example table', 'friend in friends').column('friend.name')). expect(repeater('.doc-example table', 'friend in friends').column('friend.name')).
toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']); toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']);
}); });
</doc:scenario>
</doc:example>
*/ */
//TODO: WTH is descend param for and how/when it should be used, how is it affected by +/- in //TODO: WTH is descend param for and how/when it should be used, how is it affected by +/- in
// predicate? the code below is impossible to read and specs are not very good. // predicate? the code below is impossible to read and specs are not very good.
@ -648,12 +674,14 @@ var angularArray = {
* @returns {Array} A new sub-array of length `limit`. * @returns {Array} A new sub-array of length `limit`.
* *
* @example * @example
<doc:example>
<doc:source>
<div ng:init="numbers = [1,2,3,4,5,6,7,8,9]"> <div ng:init="numbers = [1,2,3,4,5,6,7,8,9]">
Limit [1,2,3,4,5,6,7,8,9] to: <input name="limit" value="3"/> Limit [1,2,3,4,5,6,7,8,9] to: <input name="limit" value="3"/>
<p>Output: {{ numbers.$limitTo(limit) | json }}</p> <p>Output: {{ numbers.$limitTo(limit) | json }}</p>
</div> </div>
</doc:source>
* @scenario <doc:scenario>
it('should limit the numer array to first three items', function() { it('should limit the numer array to first three items', function() {
expect(element('.doc-example input[name=limit]').val()).toBe('3'); expect(element('.doc-example input[name=limit]').val()).toBe('3');
expect(binding('numbers.$limitTo(limit) | json')).toEqual('[1,2,3]'); expect(binding('numbers.$limitTo(limit) | json')).toEqual('[1,2,3]');
@ -663,6 +691,8 @@ var angularArray = {
input('limit').enter(-3); input('limit').enter(-3);
expect(binding('numbers.$limitTo(limit) | json')).toEqual('[7,8,9]'); expect(binding('numbers.$limitTo(limit) | json')).toEqual('[7,8,9]');
}); });
</doc:scenario>
</doc:example>
*/ */
limitTo: function(array, limit) { limitTo: function(array, limit) {
limit = parseInt(limit, 10); limit = parseInt(limit, 10);

View file

@ -11,15 +11,19 @@
* @param {expression} expression to eval. * @param {expression} expression to eval.
* *
* @example * @example
<doc:example>
<doc:source>
<div ng:init="greeting='Hello'; person='World'"> <div ng:init="greeting='Hello'; person='World'">
{{greeting}} {{person}}! {{greeting}} {{person}}!
</div> </div>
* </doc:source>
* @scenario <doc:scenario>
it('should check greeting', function(){ it('should check greeting', function(){
expect(binding('greeting')).toBe('Hello'); expect(binding('greeting')).toBe('Hello');
expect(binding('person')).toBe('World'); expect(binding('person')).toBe('World');
}); });
</doc:scenario>
</doc:example>
*/ */
angularDirective("ng:init", function(expression){ angularDirective("ng:init", function(expression){
return function(element){ return function(element){
@ -42,6 +46,8 @@ angularDirective("ng:init", function(expression){
* @param {expression} expression to eval. * @param {expression} expression to eval.
* *
* @example * @example
<doc:example>
<doc:source>
<script type="text/javascript"> <script type="text/javascript">
function SettingsController() { function SettingsController() {
this.name = "John Smith"; this.name = "John Smith";
@ -82,8 +88,8 @@ angularDirective("ng:init", function(expression){
<li>[ <a href="" ng:click="addContact()">add</a> ]</li> <li>[ <a href="" ng:click="addContact()">add</a> ]</li>
</ul> </ul>
</div> </div>
* </doc:source>
* @scenario <doc:scenario>
it('should check controller', function(){ it('should check controller', function(){
expect(element('.doc-example-live div>:input').val()).toBe('John Smith'); expect(element('.doc-example-live div>:input').val()).toBe('John Smith');
expect(element('.doc-example-live li[ng\\:repeat-index="0"] input').val()).toBe('408 555 1212'); expect(element('.doc-example-live li[ng\\:repeat-index="0"] input').val()).toBe('408 555 1212');
@ -93,6 +99,8 @@ angularDirective("ng:init", function(expression){
element('.doc-example-live li:last a:contains("add")').click(); element('.doc-example-live li:last a:contains("add")').click();
expect(element('.doc-example-live li[ng\\:repeat-index="2"] input').val()).toBe('yourname@example.org'); expect(element('.doc-example-live li[ng\\:repeat-index="2"] input').val()).toBe('yourname@example.org');
}); });
</doc:scenario>
</doc:example>
*/ */
angularDirective("ng:controller", function(expression){ angularDirective("ng:controller", function(expression){
this.scope(true); this.scope(true);
@ -118,23 +126,23 @@ angularDirective("ng:controller", function(expression){
* @element ANY * @element ANY
* @param {expression} expression to eval. * @param {expression} expression to eval.
* *
* @exampleDescription * @example
* Notice that `{{` `obj.multiplied = obj.a * obj.b` `}}` has a side effect of assigning * Notice that `{{` `obj.multiplied = obj.a * obj.b` `}}` has a side effect of assigning
* a value to `obj.multiplied` and displaying the result to the user. Sometimes, * a value to `obj.multiplied` and displaying the result to the user. Sometimes,
* however, it is desirable to execute a side effect without showing the value to * however, it is desirable to execute a side effect without showing the value to
* the user. In such a case `ng:eval` allows you to execute code without updating * the user. In such a case `ng:eval` allows you to execute code without updating
* the display. * the display.
* <doc:example>
* @example <doc:source>
* <input name="obj.a" value="6" > <input name="obj.a" value="6" >
* * <input name="obj.b" value="2"> * <input name="obj.b" value="2">
* = {{obj.multiplied = obj.a * obj.b}} <br> = {{obj.multiplied = obj.a * obj.b}} <br>
* <span ng:eval="obj.divide = obj.a / obj.b"></span> <span ng:eval="obj.divide = obj.a / obj.b"></span>
* <span ng:eval="obj.updateCount = 1 + (obj.updateCount||0)"></span> <span ng:eval="obj.updateCount = 1 + (obj.updateCount||0)"></span>
* <tt>obj.divide = {{obj.divide}}</tt><br/> <tt>obj.divide = {{obj.divide}}</tt><br/>
* <tt>obj.updateCount = {{obj.updateCount}}</tt> <tt>obj.updateCount = {{obj.updateCount}}</tt>
* </doc:source>
* @scenario <doc:scenario>
it('should check eval', function(){ it('should check eval', function(){
expect(binding('obj.divide')).toBe('3'); expect(binding('obj.divide')).toBe('3');
expect(binding('obj.updateCount')).toBe('2'); expect(binding('obj.updateCount')).toBe('2');
@ -142,6 +150,8 @@ angularDirective("ng:controller", function(expression){
expect(binding('obj.divide')).toBe('6'); expect(binding('obj.divide')).toBe('6');
expect(binding('obj.updateCount')).toBe('3'); expect(binding('obj.updateCount')).toBe('3');
}); });
</doc:scenario>
</doc:example>
*/ */
angularDirective("ng:eval", function(expression){ angularDirective("ng:eval", function(expression){
return function(element){ return function(element){
@ -164,18 +174,21 @@ angularDirective("ng:eval", function(expression){
* @element ANY * @element ANY
* @param {expression} expression to eval. * @param {expression} expression to eval.
* *
* @exampleDescription
* Try it here: enter text in text box and watch the greeting change.
* @example * @example
* Enter name: <input type="text" name="name" value="Whirled">. <br> * Try it here: enter text in text box and watch the greeting change.
* Hello <span ng:bind="name" />! <doc:example>
* <doc:source>
* @scenario Enter name: <input type="text" name="name" value="Whirled">. <br>
Hello <span ng:bind="name" />!
</doc:source>
<doc:scenario>
it('should check ng:bind', function(){ it('should check ng:bind', function(){
expect(using('.doc-example-live').binding('name')).toBe('Whirled'); expect(using('.doc-example-live').binding('name')).toBe('Whirled');
using('.doc-example-live').input('name').enter('world'); using('.doc-example-live').input('name').enter('world');
expect(using('.doc-example-live').binding('name')).toBe('world'); expect(using('.doc-example-live').binding('name')).toBe('world');
}); });
</doc:scenario>
</doc:example>
*/ */
angularDirective("ng:bind", function(expression, element){ angularDirective("ng:bind", function(expression, element){
element.addClass('ng-binding'); element.addClass('ng-binding');
@ -269,14 +282,15 @@ function compileBindTemplate(template){
* @param {string} template of form * @param {string} template of form
* <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval. * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
* *
* @exampleDescription
* Try it here: enter text in text box and watch the greeting change.
* @example * @example
* Try it here: enter text in text box and watch the greeting change.
<doc:example>
<doc:source>
Salutation: <input type="text" name="salutation" value="Hello"><br/> Salutation: <input type="text" name="salutation" value="Hello"><br/>
Name: <input type="text" name="name" value="World"><br/> Name: <input type="text" name="name" value="World"><br/>
<pre ng:bind-template="{{salutation}} {{name}}!"></pre> <pre ng:bind-template="{{salutation}} {{name}}!"></pre>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:bind', function(){ it('should check ng:bind', function(){
expect(using('.doc-example-live').binding('{{salutation}} {{name}}')). expect(using('.doc-example-live').binding('{{salutation}} {{name}}')).
toBe('Hello World!'); toBe('Hello World!');
@ -285,6 +299,8 @@ function compileBindTemplate(template){
expect(using('.doc-example-live').binding('{{salutation}} {{name}}')). expect(using('.doc-example-live').binding('{{salutation}} {{name}}')).
toBe('Greetings user!'); toBe('Greetings user!');
}); });
</doc:scenario>
</doc:example>
*/ */
angularDirective("ng:bind-template", function(expression, element){ angularDirective("ng:bind-template", function(expression, element){
element.addClass('ng-binding'); element.addClass('ng-binding');
@ -341,14 +357,15 @@ var REMOVE_ATTRIBUTES = {
* <tt ng:non-bindable>{{expression}}</tt>s. Any number of * <tt ng:non-bindable>{{expression}}</tt>s. Any number of
* key-value pairs can be specified. * key-value pairs can be specified.
* *
* @exampleDescription
* Try it here: enter text in text box and click Google.
* @example * @example
* Try it here: enter text in text box and click Google.
<doc:example>
<doc:source>
Google for: Google for:
<input type="text" name="query" value="AngularJS"/> <input type="text" name="query" value="AngularJS"/>
<a href="http://www.google.com/search?q={{query}}">Google</a> <a href="http://www.google.com/search?q={{query}}">Google</a>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:bind-attr', function(){ it('should check ng:bind-attr', function(){
expect(using('.doc-example-live').element('a').attr('href')). expect(using('.doc-example-live').element('a').attr('href')).
toBe('http://www.google.com/search?q=AngularJS'); toBe('http://www.google.com/search?q=AngularJS');
@ -356,6 +373,8 @@ var REMOVE_ATTRIBUTES = {
expect(using('.doc-example-live').element('a').attr('href')). expect(using('.doc-example-live').element('a').attr('href')).
toBe('http://www.google.com/search?q=google'); toBe('http://www.google.com/search?q=google');
}); });
</doc:scenario>
</doc:example>
*/ */
angularDirective("ng:bind-attr", function(expression){ angularDirective("ng:bind-attr", function(expression){
return function(element){ return function(element){
@ -403,16 +422,21 @@ angularDirective("ng:bind-attr", function(expression){
* @param {expression} expression to eval upon click. * @param {expression} expression to eval upon click.
* *
* @example * @example
<doc:example>
<doc:source>
<button ng:click="count = count + 1" ng:init="count=0"> <button ng:click="count = count + 1" ng:init="count=0">
Increment Increment
</button> </button>
count: {{count}} count: {{count}}
* @scenario </doc:source>
<doc:scenario>
it('should check ng:click', function(){ it('should check ng:click', function(){
expect(binding('count')).toBe('0'); expect(binding('count')).toBe('0');
element('.doc-example-live :button').click(); element('.doc-example-live :button').click();
expect(binding('count')).toBe('1'); expect(binding('count')).toBe('1');
}); });
</doc:scenario>
</doc:example>
*/ */
/* /*
* A directive that allows creation of custom onclick handlers that are defined as angular * A directive that allows creation of custom onclick handlers that are defined as angular
@ -440,18 +464,24 @@ angularDirective("ng:click", function(expression, element){
* @name angular.directive.ng:submit * @name angular.directive.ng:submit
* *
* @description * @description
* Enables binding angular expressions to onsubmit events.
*
* Additionally it prevents the default action (which for form means sending the request to the
* server and reloading the current page).
* *
* @element form * @element form
* @param {expression} expression to eval. * @param {expression} expression to eval.
* *
* @exampleDescription
* @example * @example
* <form ng:submit="list.push(text);text='';" ng:init="list=[]"> <doc:example>
* Enter text and hit enter: <doc:source>
* <input type="text" name="text" value="hello"/> <form ng:submit="list.push(text);text='';" ng:init="list=[]">
* </form> Enter text and hit enter:
* <pre>list={{list}}</pre> <input type="text" name="text" value="hello"/>
* @scenario </form>
<pre>list={{list}}</pre>
</doc:source>
<doc:scenario>
it('should check ng:submit', function(){ it('should check ng:submit', function(){
expect(binding('list')).toBe('list=[]'); expect(binding('list')).toBe('list=[]');
element('.doc-example-live form input').click(); element('.doc-example-live form input').click();
@ -463,12 +493,8 @@ angularDirective("ng:click", function(expression, element){
}); });
expect(binding('list')).toBe('list=["hello"]'); expect(binding('list')).toBe('list=["hello"]');
}); });
*/ </doc:scenario>
/** </doc:example>
* Enables binding angular expressions to onsubmit events.
*
* Additionally it prevents the default action (which for form means sending the request to the
* server and reloading the current page).
*/ */
angularDirective("ng:submit", function(expression, element) { angularDirective("ng:submit", function(expression, element) {
return injectUpdateView(function($updateView, element) { return injectUpdateView(function($updateView, element) {
@ -494,20 +520,24 @@ angularDirective("ng:submit", function(expression, element) {
* @element ANY * @element ANY
* @param {expression} expression to eval. * @param {expression} expression to eval.
* *
* @exampleDescription * @example
* Notice that the counter is incremented * Notice that the counter is incremented
* every time you change the text. * every time you change the text.
* @example <doc:example>
<doc:source>
<div ng:init="counter=0" ng:watch="name: counter = counter+1"> <div ng:init="counter=0" ng:watch="name: counter = counter+1">
<input type="text" name="name" value="hello"><br/> <input type="text" name="name" value="hello"><br/>
Change counter: {{counter}} Name: {{name}} Change counter: {{counter}} Name: {{name}}
</div> </div>
* @scenario </doc:source>
<doc:scenario>
it('should check ng:watch', function(){ it('should check ng:watch', function(){
expect(using('.doc-example-live').binding('counter')).toBe('2'); expect(using('.doc-example-live').binding('counter')).toBe('2');
using('.doc-example-live').input('name').enter('abc'); using('.doc-example-live').input('name').enter('abc');
expect(using('.doc-example-live').binding('counter')).toBe('3'); expect(using('.doc-example-live').binding('counter')).toBe('3');
}); });
</doc:scenario>
</doc:example>
*/ */
//TODO: delete me, since having watch in UI is logic in UI. (leftover form getangular) //TODO: delete me, since having watch in UI is logic in UI. (leftover form getangular)
angularDirective("ng:watch", function(expression, element){ angularDirective("ng:watch", function(expression, element){
@ -550,14 +580,15 @@ function ngClass(selector) {
* @element ANY * @element ANY
* @param {expression} expression to eval. * @param {expression} expression to eval.
* *
* @exampleDescription
* @example * @example
<doc:example>
<doc:source>
<input type="button" value="set" ng:click="myVar='ng-input-indicator-wait'"> <input type="button" value="set" ng:click="myVar='ng-input-indicator-wait'">
<input type="button" value="clear" ng:click="myVar=''"> <input type="button" value="clear" ng:click="myVar=''">
<br> <br>
<span ng:class="myVar">Sample Text &nbsp;&nbsp;&nbsp;&nbsp;</span> <span ng:class="myVar">Sample Text &nbsp;&nbsp;&nbsp;&nbsp;</span>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:class', function(){ it('should check ng:class', function(){
expect(element('.doc-example-live span').attr('className')).not(). expect(element('.doc-example-live span').attr('className')).not().
toMatch(/ng-input-indicator-wait/); toMatch(/ng-input-indicator-wait/);
@ -572,6 +603,8 @@ function ngClass(selector) {
expect(element('.doc-example-live span').attr('className')).not(). expect(element('.doc-example-live span').attr('className')).not().
toMatch(/ng-input-indicator-wait/); toMatch(/ng-input-indicator-wait/);
}); });
</doc:scenario>
</doc:example>
*/ */
angularDirective("ng:class", ngClass(function(){return true;})); angularDirective("ng:class", ngClass(function(){return true;}));
@ -588,10 +621,10 @@ angularDirective("ng:class", ngClass(function(){return true;}));
* @element ANY * @element ANY
* @param {expression} expression to eval. Must be inside * @param {expression} expression to eval. Must be inside
* `ng:repeat`. * `ng:repeat`.
* *
* @exampleDescription
* @example * @example
<doc:example>
<doc:source>
<ol ng:init="names=['John', 'Mary', 'Cate', 'Suz']"> <ol ng:init="names=['John', 'Mary', 'Cate', 'Suz']">
<li ng:repeat="name in names"> <li ng:repeat="name in names">
<span ng:class-odd="'ng-format-negative'" <span ng:class-odd="'ng-format-negative'"
@ -600,14 +633,16 @@ angularDirective("ng:class", ngClass(function(){return true;}));
</span> </span>
</li> </li>
</ol> </ol>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:class-odd and ng:class-even', function(){ it('should check ng:class-odd and ng:class-even', function(){
expect(element('.doc-example-live li:first span').attr('className')). expect(element('.doc-example-live li:first span').attr('className')).
toMatch(/ng-format-negative/); toMatch(/ng-format-negative/);
expect(element('.doc-example-live li:last span').attr('className')). expect(element('.doc-example-live li:last span').attr('className')).
toMatch(/ng-input-indicator-wait/); toMatch(/ng-input-indicator-wait/);
}); });
</doc:scenario>
</doc:example>
*/ */
angularDirective("ng:class-odd", ngClass(function(i){return i % 2 === 0;})); angularDirective("ng:class-odd", ngClass(function(i){return i % 2 === 0;}));
@ -624,10 +659,10 @@ angularDirective("ng:class-odd", ngClass(function(i){return i % 2 === 0;}));
* @element ANY * @element ANY
* @param {expression} expression to eval. Must be inside * @param {expression} expression to eval. Must be inside
* `ng:repeat`. * `ng:repeat`.
* *
* @exampleDescription
* @example * @example
<doc:example>
<doc:source>
<ol ng:init="names=['John', 'Mary', 'Cate', 'Suz']"> <ol ng:init="names=['John', 'Mary', 'Cate', 'Suz']">
<li ng:repeat="name in names"> <li ng:repeat="name in names">
<span ng:class-odd="'ng-format-negative'" <span ng:class-odd="'ng-format-negative'"
@ -636,14 +671,16 @@ angularDirective("ng:class-odd", ngClass(function(i){return i % 2 === 0;}));
</span> </span>
</li> </li>
</ol> </ol>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:class-odd and ng:class-even', function(){ it('should check ng:class-odd and ng:class-even', function(){
expect(element('.doc-example-live li:first span').attr('className')). expect(element('.doc-example-live li:first span').attr('className')).
toMatch(/ng-format-negative/); toMatch(/ng-format-negative/);
expect(element('.doc-example-live li:last span').attr('className')). expect(element('.doc-example-live li:last span').attr('className')).
toMatch(/ng-input-indicator-wait/); toMatch(/ng-input-indicator-wait/);
}); });
</doc:scenario>
</doc:example>
*/ */
angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;})); angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;}));
@ -660,13 +697,14 @@ angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;}));
* @param {expression} expression if truthy then the element is * @param {expression} expression if truthy then the element is
* shown or hidden respectively. * shown or hidden respectively.
* *
* @exampleDescription
* @example * @example
<doc:example>
<doc:source>
Click me: <input type="checkbox" name="checked"><br/> Click me: <input type="checkbox" name="checked"><br/>
Show: <span ng:show="checked">I show up when you checkbox is checked?</span> <br/> Show: <span ng:show="checked">I show up when you checkbox is checked?</span> <br/>
Hide: <span ng:hide="checked">I hide when you checkbox is checked?</span> Hide: <span ng:hide="checked">I hide when you checkbox is checked?</span>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:show / ng:hide', function(){ it('should check ng:show / ng:hide', function(){
expect(element('.doc-example-live span:first:hidden').count()).toEqual(1); expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
expect(element('.doc-example-live span:last:visible').count()).toEqual(1); expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
@ -676,6 +714,8 @@ angularDirective("ng:class-even", ngClass(function(i){return i % 2 === 1;}));
expect(element('.doc-example-live span:first:visible').count()).toEqual(1); expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
expect(element('.doc-example-live span:last:hidden').count()).toEqual(1); expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
}); });
</doc:scenario>
</doc:example>
*/ */
angularDirective("ng:show", function(expression, element){ angularDirective("ng:show", function(expression, element){
return function(element){ return function(element){
@ -698,13 +738,14 @@ angularDirective("ng:show", function(expression, element){
* @param {expression} expression if truthy then the element is * @param {expression} expression if truthy then the element is
* shown or hidden respectively. * shown or hidden respectively.
* *
* @exampleDescription
* @example * @example
<doc:example>
<doc:source>
Click me: <input type="checkbox" name="checked"><br/> Click me: <input type="checkbox" name="checked"><br/>
Show: <span ng:show="checked">I show up when you checkbox is checked?</span> <br/> Show: <span ng:show="checked">I show up when you checkbox is checked?</span> <br/>
Hide: <span ng:hide="checked">I hide when you checkbox is checked?</span> Hide: <span ng:hide="checked">I hide when you checkbox is checked?</span>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:show / ng:hide', function(){ it('should check ng:show / ng:hide', function(){
expect(element('.doc-example-live span:first:hidden').count()).toEqual(1); expect(element('.doc-example-live span:first:hidden').count()).toEqual(1);
expect(element('.doc-example-live span:last:visible').count()).toEqual(1); expect(element('.doc-example-live span:last:visible').count()).toEqual(1);
@ -714,6 +755,8 @@ angularDirective("ng:show", function(expression, element){
expect(element('.doc-example-live span:first:visible').count()).toEqual(1); expect(element('.doc-example-live span:first:visible').count()).toEqual(1);
expect(element('.doc-example-live span:last:hidden').count()).toEqual(1); expect(element('.doc-example-live span:last:hidden').count()).toEqual(1);
}); });
</doc:scenario>
</doc:example>
*/ */
angularDirective("ng:hide", function(expression, element){ angularDirective("ng:hide", function(expression, element){
return function(element){ return function(element){
@ -736,15 +779,16 @@ angularDirective("ng:hide", function(expression, element){
* CSS style names and values are coresponding values for those * CSS style names and values are coresponding values for those
* CSS keys. * CSS keys.
* *
* @exampleDescription
* @example * @example
<doc:example>
<doc:source>
<input type="button" value="set" ng:click="myStyle={color:'red'}"> <input type="button" value="set" ng:click="myStyle={color:'red'}">
<input type="button" value="clear" ng:click="myStyle={}"> <input type="button" value="clear" ng:click="myStyle={}">
<br/> <br/>
<span ng:style="myStyle">Sample Text</span> <span ng:style="myStyle">Sample Text</span>
<pre>myStyle={{myStyle}}</pre> <pre>myStyle={{myStyle}}</pre>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:style', function(){ it('should check ng:style', function(){
expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)'); expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
element('.doc-example-live :button[value=set]').click(); element('.doc-example-live :button[value=set]').click();
@ -752,6 +796,8 @@ angularDirective("ng:hide", function(expression, element){
element('.doc-example-live :button[value=clear]').click(); element('.doc-example-live :button[value=clear]').click();
expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)'); expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)');
}); });
</doc:scenario>
</doc:example>
*/ */
angularDirective("ng:style", function(expression, element){ angularDirective("ng:style", function(expression, element){
return function(element){ return function(element){

View file

@ -14,10 +14,12 @@
* 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 * @example
<doc:example>
<doc:source>
<input type="text" name="amount" value="1234.56"/> <br/> <input type="text" name="amount" value="1234.56"/> <br/>
{{amount | currency}} {{amount | currency}}
* </doc:source>
* @scenario <doc:scenario>
it('should init with 1234.56', function(){ it('should init with 1234.56', function(){
expect(binding('amount | currency')).toBe('$1,234.56'); expect(binding('amount | currency')).toBe('$1,234.56');
}); });
@ -27,6 +29,8 @@
expect(element('.doc-example-live .ng-binding').attr('className')). expect(element('.doc-example-live .ng-binding').attr('className')).
toMatch(/ng-format-negative/); toMatch(/ng-format-negative/);
}); });
</doc:scenario>
</doc:example>
*/ */
angularFilter.currency = function(amount){ angularFilter.currency = function(amount){
this.$element.toggleClass('ng-format-negative', amount < 0); this.$element.toggleClass('ng-format-negative', amount < 0);
@ -49,12 +53,14 @@ angularFilter.currency = function(amount){
* @returns {string} Number rounded to decimalPlaces and places a , after each third digit. * @returns {string} Number rounded to decimalPlaces and places a , after each third digit.
* *
* @example * @example
<doc:example>
<doc:source>
Enter number: <input name='val' value='1234.56789' /><br/> Enter number: <input name='val' value='1234.56789' /><br/>
Default formatting: {{val | number}}<br/> Default formatting: {{val | number}}<br/>
No fractions: {{val | number:0}}<br/> No fractions: {{val | number:0}}<br/>
Negative number: {{-val | number:4}} Negative number: {{-val | number:4}}
</doc:source>
* @scenario <doc:scenario>
it('should format numbers', function(){ it('should format numbers', function(){
expect(binding('val | number')).toBe('1,234.57'); expect(binding('val | number')).toBe('1,234.57');
expect(binding('val | number:0')).toBe('1,235'); expect(binding('val | number:0')).toBe('1,235');
@ -67,6 +73,8 @@ angularFilter.currency = function(amount){
expect(binding('val | number:0')).toBe('3,374'); expect(binding('val | number:0')).toBe('3,374');
expect(binding('-val | number:4')).toBe('-3,374.3330'); expect(binding('-val | number:4')).toBe('-3,374.3330');
}); });
</doc:scenario>
</doc:example>
*/ */
angularFilter.number = function(number, fractionSize){ angularFilter.number = function(number, fractionSize){
if (isNaN(number) || !isFinite(number)) { if (isNaN(number) || !isFinite(number)) {
@ -183,19 +191,22 @@ var NUMBER_STRING = /^\d+$/;
* @returns {string} Formatted string or the input if input is not recognized as date/millis. * @returns {string} Formatted string or the input if input is not recognized as date/millis.
* *
* @example * @example
<doc:example>
<doc:source>
<span ng:non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>: <span ng:non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}<br/> {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}<br/>
<span ng:non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>: <span ng:non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}<br/> {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}<br/>
* </doc:source>
* @scenario <doc:scenario>
it('should format date', function(){ it('should format date', function(){
expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")). expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).
toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} \-?\d{4}/); toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} \-?\d{4}/);
expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")). expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).
toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(am|pm)/); toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(am|pm)/);
}); });
* </doc:scenario>
</doc:example>
*/ */
angularFilter.date = function(date, format) { angularFilter.date = function(date, format) {
if (isString(date)) { if (isString(date)) {
@ -255,11 +266,13 @@ angularFilter.date = function(date, format) {
* @css ng-monospace Always applied to the encapsulating element. * @css ng-monospace Always applied to the encapsulating element.
* *
* @example: * @example:
<doc:example>
<doc:source>
<input type="text" name="objTxt" value="{a:1, b:[]}" <input type="text" name="objTxt" value="{a:1, b:[]}"
ng:eval="obj = $eval(objTxt)"/> ng:eval="obj = $eval(objTxt)"/>
<pre>{{ obj | json }}</pre> <pre>{{ obj | json }}</pre>
* </doc:source>
* @scenario <doc:scenario>
it('should jsonify filtered objects', function() { it('should jsonify filtered objects', function() {
expect(binding('obj | json')).toBe('{\n "a":1,\n "b":[]}'); expect(binding('obj | json')).toBe('{\n "a":1,\n "b":[]}');
}); });
@ -268,6 +281,8 @@ angularFilter.date = function(date, format) {
input('objTxt').enter('[1, 2, 3]'); input('objTxt').enter('[1, 2, 3]');
expect(binding('obj | json')).toBe('[1,2,3]'); expect(binding('obj | json')).toBe('[1,2,3]');
}); });
</doc:scenario>
</doc:example>
* *
*/ */
angularFilter.json = function(object) { angularFilter.json = function(object) {
@ -324,6 +339,8 @@ angularFilter.uppercase = uppercase;
* @returns {string} Sanitized or raw html. * @returns {string} Sanitized or raw html.
* *
* @example * @example
<doc:example>
<doc:source>
Snippet: <textarea name="snippet" cols="60" rows="3"> Snippet: <textarea name="snippet" cols="60" rows="3">
&lt;p style="color:blue"&gt;an html &lt;p style="color:blue"&gt;an html
&lt;em onmouseover="this.textContent='PWN3D!'"&gt;click here&lt;/em&gt; &lt;em onmouseover="this.textContent='PWN3D!'"&gt;click here&lt;/em&gt;
@ -354,8 +371,8 @@ snippet&lt;/p&gt;</textarea>
<td><div ng:bind="snippet | html:'unsafe'"></div></td> <td><div ng:bind="snippet | html:'unsafe'"></div></td>
</tr> </tr>
</table> </table>
* </doc:source>
* @scenario <doc:scenario>
it('should sanitize the html snippet ', function(){ it('should sanitize the html snippet ', function(){
expect(using('#html-filter').binding('snippet | html')). expect(using('#html-filter').binding('snippet | html')).
toBe('<p>an html\n<em>click here</em>\nsnippet</p>'); toBe('<p>an html\n<em>click here</em>\nsnippet</p>');
@ -381,6 +398,8 @@ snippet&lt;/p&gt;</textarea>
expect(using('#escaped-html').binding('snippet')).toBe("new &lt;b&gt;text&lt;/b&gt;"); expect(using('#escaped-html').binding('snippet')).toBe("new &lt;b&gt;text&lt;/b&gt;");
expect(using('#html-unsafe-filter').binding("snippet | html:'unsafe'")).toBe('new <b>text</b>'); expect(using('#html-unsafe-filter').binding("snippet | html:'unsafe'")).toBe('new <b>text</b>');
}); });
</doc:scenario>
</doc:example>
*/ */
angularFilter.html = function(html, option){ angularFilter.html = function(html, option){
return new HTML(html, option); return new HTML(html, option);
@ -401,6 +420,8 @@ angularFilter.html = function(html, option){
* @returns {string} Html-linkified text. * @returns {string} Html-linkified text.
* *
* @example * @example
<doc:example>
<doc:source>
Snippet: <textarea name="snippet" cols="60" rows="3"> Snippet: <textarea name="snippet" cols="60" rows="3">
Pretty text with some links: Pretty text with some links:
http://angularjs.org/, http://angularjs.org/,
@ -428,8 +449,8 @@ and one more: ftp://127.0.0.1/.</textarea>
<td><div ng:bind="snippet"></div></td> <td><div ng:bind="snippet"></div></td>
</tr> </tr>
</table> </table>
</doc:source>
@scenario <doc:scenario>
it('should linkify the snippet with urls', function(){ it('should linkify the snippet with urls', function(){
expect(using('#linky-filter').binding('snippet | linky')). expect(using('#linky-filter').binding('snippet | linky')).
toBe('Pretty text with some links:\n' + toBe('Pretty text with some links:\n' +
@ -454,6 +475,8 @@ and one more: ftp://127.0.0.1/.</textarea>
toBe('new <a href="http://link">http://link</a>.'); toBe('new <a href="http://link">http://link</a>.');
expect(using('#escaped-html').binding('snippet')).toBe('new http://link.'); expect(using('#escaped-html').binding('snippet')).toBe('new http://link.');
}); });
</doc:scenario>
</doc:example>
*/ */
//TODO: externalize all regexps //TODO: externalize all regexps
angularFilter.linky = function(text){ angularFilter.linky = function(text){

View file

@ -18,17 +18,21 @@ angularFormatter.noop = formatter(identity, identity);
* @returns {?string} A JSON string representation of the model. * @returns {?string} A JSON string representation of the model.
* *
* @example * @example
* <div ng:init="data={name:'misko', project:'angular'}"> <doc:example>
* <input type="text" size='50' name="data" ng:format="json"/> <doc:source>
* <pre>data={{data}}</pre> <div ng:init="data={name:'misko', project:'angular'}">
* </div> <input type="text" size='50' name="data" ng:format="json"/>
* <pre>data={{data}}</pre>
* @scenario </div>
* it('should format json', function(){ </doc:source>
* expect(binding('data')).toEqual('data={\n \"name\":\"misko\",\n \"project\":\"angular\"}'); <doc:scenario>
* input('data').enter('{}'); it('should format json', function(){
* expect(binding('data')).toEqual('data={\n }'); expect(binding('data')).toEqual('data={\n \"name\":\"misko\",\n \"project\":\"angular\"}');
* }); input('data').enter('{}');
expect(binding('data')).toEqual('data={\n }');
});
</doc:scenario>
</doc:example>
*/ */
angularFormatter.json = formatter(toJson, function(value){ angularFormatter.json = formatter(toJson, function(value){
return fromJson(value || 'null'); return fromJson(value || 'null');
@ -45,17 +49,21 @@ angularFormatter.json = formatter(toJson, function(value){
* @returns {boolean} Converts to `true` unless user enters (blank), `f`, `false`, `0`, `no`, `[]`. * @returns {boolean} Converts to `true` unless user enters (blank), `f`, `false`, `0`, `no`, `[]`.
* *
* @example * @example
* Enter truthy text: <doc:example>
* <input type="text" name="value" ng:format="boolean" value="no"/> <doc:source>
* <input type="checkbox" name="value"/> Enter truthy text:
* <pre>value={{value}}</pre> <input type="text" name="value" ng:format="boolean" value="no"/>
* <input type="checkbox" name="value"/>
* @scenario <pre>value={{value}}</pre>
* it('should format boolean', function(){ </doc:source>
* expect(binding('value')).toEqual('value=false'); <doc:scenario>
* input('value').enter('truthy'); it('should format boolean', function(){
* expect(binding('value')).toEqual('value=true'); expect(binding('value')).toEqual('value=false');
* }); input('value').enter('truthy');
expect(binding('value')).toEqual('value=true');
});
</doc:scenario>
</doc:example>
*/ */
angularFormatter['boolean'] = formatter(toString, toBoolean); angularFormatter['boolean'] = formatter(toString, toBoolean);
@ -70,16 +78,20 @@ angularFormatter['boolean'] = formatter(toString, toBoolean);
* @returns {number} Number from the parsed string. * @returns {number} Number from the parsed string.
* *
* @example * @example
* Enter valid number: <doc:example>
* <input type="text" name="value" ng:format="number" value="1234"/> <doc:source>
* <pre>value={{value}}</pre> Enter valid number:
* <input type="text" name="value" ng:format="number" value="1234"/>
* @scenario <pre>value={{value}}</pre>
* it('should format numbers', function(){ </doc:source>
* expect(binding('value')).toEqual('value=1234'); <doc:scenario>
* input('value').enter('5678'); it('should format numbers', function(){
* expect(binding('value')).toEqual('value=5678'); expect(binding('value')).toEqual('value=1234');
* }); input('value').enter('5678');
expect(binding('value')).toEqual('value=5678');
});
</doc:scenario>
</doc:example>
*/ */
angularFormatter.number = formatter(toString, function(obj){ angularFormatter.number = formatter(toString, function(obj){
if (obj == _null || NUMBER.exec(obj)) { if (obj == _null || NUMBER.exec(obj)) {
@ -100,20 +112,24 @@ angularFormatter.number = formatter(toString, function(obj){
* @returns {Array} Array parsed from the entered string. * @returns {Array} Array parsed from the entered string.
* *
* @example * @example
* Enter a list of items: <doc:example>
* <input type="text" name="value" ng:format="list" value=" chair ,, table"/> <doc:source>
* <input type="text" name="value" ng:format="list"/> Enter a list of items:
* <pre>value={{value}}</pre> <input type="text" name="value" ng:format="list" value=" chair ,, table"/>
* <input type="text" name="value" ng:format="list"/>
* @scenario <pre>value={{value}}</pre>
* it('should format lists', function(){ </doc:source>
* expect(binding('value')).toEqual('value=["chair","table"]'); <doc:scenario>
* this.addFutureAction('change to XYZ', function($window, $document, done){ it('should format lists', function(){
* $document.elements('.doc-example :input:last').val(',,a,b,').trigger('change'); expect(binding('value')).toEqual('value=["chair","table"]');
* done(); this.addFutureAction('change to XYZ', function($window, $document, done){
* }); $document.elements('.doc-example :input:last').val(',,a,b,').trigger('change');
* expect(binding('value')).toEqual('value=["a","b"]'); done();
* }); });
expect(binding('value')).toEqual('value=["a","b"]');
});
</doc:scenario>
</doc:example>
*/ */
angularFormatter.list = formatter( angularFormatter.list = formatter(
function(obj) { return obj ? obj.join(", ") : obj; }, function(obj) { return obj ? obj.join(", ") : obj; },
@ -138,20 +154,24 @@ angularFormatter.list = formatter(
* @returns {String} Trim excess leading and trailing space. * @returns {String} Trim excess leading and trailing space.
* *
* @example * @example
* Enter text with leading/trailing spaces: <doc:example>
* <input type="text" name="value" ng:format="trim" value=" book "/> <doc:source>
* <input type="text" name="value" ng:format="trim"/> Enter text with leading/trailing spaces:
* <pre>value={{value|json}}</pre> <input type="text" name="value" ng:format="trim" value=" book "/>
* <input type="text" name="value" ng:format="trim"/>
* @scenario <pre>value={{value|json}}</pre>
* it('should format trim', function(){ </doc:source>
* expect(binding('value')).toEqual('value="book"'); <doc:scenario>
* this.addFutureAction('change to XYZ', function($window, $document, done){ it('should format trim', function(){
* $document.elements('.doc-example :input:last').val(' text ').trigger('change'); expect(binding('value')).toEqual('value="book"');
* done(); this.addFutureAction('change to XYZ', function($window, $document, done){
* }); $document.elements('.doc-example :input:last').val(' text ').trigger('change');
* expect(binding('value')).toEqual('value="text"'); done();
* }); });
expect(binding('value')).toEqual('value="text"');
});
</doc:scenario>
</doc:example>
*/ */
angularFormatter.trim = formatter( angularFormatter.trim = formatter(
function(obj) { return obj ? trim("" + obj) : ""; } function(obj) { return obj ? trim("" + obj) : ""; }
@ -176,33 +196,36 @@ angularFormatter.trim = formatter(
* @returns {object} object which is located at the selected position. * @returns {object} object which is located at the selected position.
* *
* @example * @example
* <script> <doc:example>
* function DemoCntl(){ <doc:source>
* this.users = [ <script>
* {name:'guest', password:'guest'}, function DemoCntl(){
* {name:'user', password:'123'}, this.users = [
* {name:'admin', password:'abc'} {name:'guest', password:'guest'},
* ]; {name:'user', password:'123'},
* } {name:'admin', password:'abc'}
* </script> ];
* <div ng:controller="DemoCntl"> }
* User: </script>
* <select name="currentUser" ng:format="index:users"> <div ng:controller="DemoCntl">
* <option ng:repeat="user in users" value="{{$index}}">{{user.name}}</option> User:
* </select> <select name="currentUser" ng:format="index:users">
* <select name="currentUser" ng:format="index:users"> <option ng:repeat="user in users" value="{{$index}}">{{user.name}}</option>
* <option ng:repeat="user in users" value="{{$index}}">{{user.name}}</option> </select>
* </select> <select name="currentUser" ng:format="index:users">
* user={{currentUser.name}}<br/> <option ng:repeat="user in users" value="{{$index}}">{{user.name}}</option>
* password={{currentUser.password}}<br/> </select>
* </div> user={{currentUser.name}}<br/>
* password={{currentUser.password}}<br/>
* @scenario </doc:source>
* it('should retrieve object by index', function(){ <doc:scenario>
* expect(binding('currentUser.password')).toEqual('guest'); it('should retrieve object by index', function(){
* select('currentUser').option('2'); expect(binding('currentUser.password')).toEqual('guest');
* expect(binding('currentUser.password')).toEqual('abc'); select('currentUser').option('2');
* }); expect(binding('currentUser.password')).toEqual('abc');
});
</doc:scenario>
</doc:example>
*/ */
angularFormatter.index = formatter( angularFormatter.index = formatter(
function(object, array){ function(object, array){

View file

@ -22,8 +22,19 @@ function angularServiceInject(name, fn, inject, eager) {
* suffer from window globality. * suffer from window globality.
* *
* @example * @example
<<<<<<< HEAD
<input ng:init="$window = $service('$window'); greeting='Hello World!'" type="text" name="greeting" /> <input ng:init="$window = $service('$window'); greeting='Hello World!'" type="text" name="greeting" />
<button ng:click="$window.alert(greeting)">ALERT</button> <button ng:click="$window.alert(greeting)">ALERT</button>
=======
<doc:example>
<doc:source>
<input ng:init="greeting='Hello World!'" type="text" name="greeting" />
<button ng:click="$window.alert(greeting)">ALERT</button>
</doc:source>
<doc:scenario>
</doc:scenario>
</doc:example>
>>>>>>> changed the documentation @example to use <doc:example>
*/ */
angularServiceInject("$window", bind(window, identity, window), [], EAGER); angularServiceInject("$window", bind(window, identity, window), [], EAGER);
@ -63,10 +74,16 @@ angularServiceInject("$document", function(window){
* Notice that using browser's forward/back buttons changes the $location. * Notice that using browser's forward/back buttons changes the $location.
* *
* @example * @example
<doc:example>
<doc:source>
<a href="#">clear hash</a> | <a href="#">clear hash</a> |
<a href="#myPath?name=misko">test hash</a><br/> <a href="#myPath?name=misko">test hash</a><br/>
<input type='text' name="$location.hash"/> <input type='text' name="$location.hash"/>
<pre>$location = {{$location}}</pre> <pre>$location = {{$location}}</pre>
</doc:source>
<doc:scenario>
</doc:scenario>
</doc:example>
*/ */
angularServiceInject("$location", function($browser) { angularServiceInject("$location", function($browser) {
var scope = this, var scope = this,
@ -98,9 +115,15 @@ angularServiceInject("$location", function($browser) {
* Browser is updated at the end of $eval() * Browser is updated at the end of $eval()
* *
* @example * @example
* scope.$location.update('http://www.angularjs.org/path#hash?search=x'); <doc:example>
* scope.$location.update({host: 'www.google.com', protocol: 'https'}); <doc:source>
* scope.$location.update({hashPath: '/path', hashSearch: {a: 'b', x: true}}); scope.$location.update('http://www.angularjs.org/path#hash?search=x');
scope.$location.update({host: 'www.google.com', protocol: 'https'});
scope.$location.update({hashPath: '/path', hashSearch: {a: 'b', x: true}});
</doc:source>
<doc:scenario>
</doc:scenario>
</doc:example>
* *
* @param {(string|Object)} href Full href as a string or object with properties * @param {(string|Object)} href Full href as a string or object with properties
*/ */
@ -133,14 +156,18 @@ angularServiceInject("$location", function($browser) {
* @see update() * @see update()
* *
* @example * @example
* scope.$location.updateHash('/hp') <doc:example>
* ==> update({hashPath: '/hp'}) <doc:source>
* scope.$location.updateHash('/hp')
* scope.$location.updateHash({a: true, b: 'val'}) ==> update({hashPath: '/hp'})
* ==> update({hashSearch: {a: true, b: 'val'}}) scope.$location.updateHash({a: true, b: 'val'})
* ==> update({hashSearch: {a: true, b: 'val'}})
* scope.$location.updateHash('/hp', {a: true}) scope.$location.updateHash('/hp', {a: true})
* ==> update({hashPath: '/hp', hashSearch: {a: true}}) ==> update({hashPath: '/hp', hashSearch: {a: true}})
</doc:source>
<doc:scenario>
</doc:scenario>
</doc:example>
* *
* @param {(string|Object)} path A hashPath or hashSearch object * @param {(string|Object)} path A hashPath or hashSearch object
* @param {Object=} search A hashSearch object * @param {Object=} search A hashSearch object
@ -176,7 +203,9 @@ angularServiceInject("$location", function($browser) {
* - everything else * - everything else
* *
* @example * @example
* <pre>
* scope.$location.href = 'http://www.angularjs.org/path#a/b' * scope.$location.href = 'http://www.angularjs.org/path#a/b'
* </pre>
* immediately after this call, other properties are still the old ones... * immediately after this call, other properties are still the old ones...
* *
* This method checks the changes and update location to the consistent state * This method checks the changes and update location to the consistent state
@ -298,6 +327,8 @@ angularServiceInject("$location", function($browser) {
* The main purpose of this service is to simplify debugging and troubleshooting. * The main purpose of this service is to simplify debugging and troubleshooting.
* *
* @example * @example
<doc:example>
<doc:source>
<p>Reload this page with open console, enter text and hit the log button...</p> <p>Reload this page with open console, enter text and hit the log button...</p>
Message: Message:
<input type="text" name="message" value="Hello World!"/> <input type="text" name="message" value="Hello World!"/>
@ -305,6 +336,10 @@ angularServiceInject("$location", function($browser) {
<button ng:click="$log.warn(message)">warn</button> <button ng:click="$log.warn(message)">warn</button>
<button ng:click="$log.info(message)">info</button> <button ng:click="$log.info(message)">info</button>
<button ng:click="$log.error(message)">error</button> <button ng:click="$log.error(message)">error</button>
</doc:source>
<doc:scenario>
</doc:scenario>
</doc:example>
*/ */
var $logFactory; //reference to be used only in tests var $logFactory; //reference to be used only in tests
angularServiceInject("$log", $logFactory = function($window){ angularServiceInject("$log", $logFactory = function($window){
@ -622,12 +657,12 @@ function switchRouteMatcher(on, when, dstName) {
* widget. * widget.
* *
* @example * @example
<p>
This example shows how changing the URL hash causes the <tt>$route</tt> This example shows how changing the URL hash causes the <tt>$route</tt>
to match a route against the URL, and the <tt>[[ng:include]]</tt> pulls in the partial. to match a route against the URL, and the <tt>[[ng:include]]</tt> pulls in the partial.
Try changing the URL in the input box to see changes. Try changing the URL in the input box to see changes.
</p>
<doc:example>
<doc:source>
<script> <script>
angular.service('myApp', function($route) { angular.service('myApp', function($route) {
$route.when('/Book/:bookId', {template:'rsrc/book.html', controller:BookCntl}); $route.when('/Book/:bookId', {template:'rsrc/book.html', controller:BookCntl});
@ -658,6 +693,10 @@ Chose:
<pre>$route.current.scope.name={{$route.current.scope.name}}</pre> <pre>$route.current.scope.name={{$route.current.scope.name}}</pre>
<hr/> <hr/>
<ng:include src="$route.current.template" scope="$route.current.scope"/> <ng:include src="$route.current.template" scope="$route.current.scope"/>
</doc:source>
<doc:scenario>
</doc:scenario>
</doc:example>
*/ */
angularServiceInject('$route', function(location) { angularServiceInject('$route', function(location) {
var routes = {}, var routes = {},
@ -1122,6 +1161,8 @@ angularServiceInject('$xhr.cache', function($xhr, $defer, $log){
* @returns {Object} A resource "class". * @returns {Object} A resource "class".
* *
* @example * @example
<doc:example>
<doc:source>
<script> <script>
function BuzzController($resource) { function BuzzController($resource) {
this.Activity = $resource( this.Activity = $resource(
@ -1159,6 +1200,10 @@ angularServiceInject('$xhr.cache', function($xhr, $defer, $log){
</div> </div>
</div> </div>
</div> </div>
</doc:source>
<doc:scenario>
</doc:scenario>
</doc:example>
*/ */
angularServiceInject('$resource', function($xhr){ angularServiceInject('$resource', function($xhr){
var resource = new ResourceFactory($xhr); var resource = new ResourceFactory($xhr);

View file

@ -14,24 +14,27 @@ extend(angularValidator, {
* @css ng-validation-error * @css ng-validation-error
* *
* @example * @example
* <script> function Cntl(){ <doc:example>
* this.ssnRegExp = /^\d\d\d-\d\d-\d\d\d\d$/; <doc:source>
* } <script> function Cntl(){
* </script> this.ssnRegExp = /^\d\d\d-\d\d-\d\d\d\d$/;
* Enter valid SSN: }
* <div ng:controller="Cntl"> </script>
* <input name="ssn" value="123-45-6789" ng:validate="regexp:ssnRegExp" > Enter valid SSN:
* </div> <div ng:controller="Cntl">
* <input name="ssn" value="123-45-6789" ng:validate="regexp:ssnRegExp" >
* @scenario </div>
* it('should invalidate non ssn', function(){ </doc:source>
* var textBox = element('.doc-example :input'); <doc:scenario>
* expect(textBox.attr('className')).not().toMatch(/ng-validation-error/); it('should invalidate non ssn', function(){
* expect(textBox.val()).toEqual('123-45-6789'); var textBox = element('.doc-example :input');
* expect(textBox.attr('className')).not().toMatch(/ng-validation-error/);
* input('ssn').enter('123-45-67890'); expect(textBox.val()).toEqual('123-45-6789');
* expect(textBox.attr('className')).toMatch(/ng-validation-error/); input('ssn').enter('123-45-67890');
* }); expect(textBox.attr('className')).toMatch(/ng-validation-error/);
});
</doc:scenario>
</doc:example>
* *
*/ */
'regexp': function(value, regexp, msg) { 'regexp': function(value, regexp, msg) {
@ -57,28 +60,29 @@ extend(angularValidator, {
* @css ng-validation-error * @css ng-validation-error
* *
* @example * @example
* Enter number: <input name="n1" ng:validate="number" > <br> <doc:example>
* Enter number greater than 10: <input name="n2" ng:validate="number:10" > <br> <doc:source>
* Enter number between 100 and 200: <input name="n3" ng:validate="number:100:200" > <br> Enter number: <input name="n1" ng:validate="number" > <br>
* Enter number greater than 10: <input name="n2" ng:validate="number:10" > <br>
* @scenario Enter number between 100 and 200: <input name="n3" ng:validate="number:100:200" > <br>
* it('should invalidate number', function(){ </doc:source>
* var n1 = element('.doc-example :input[name=n1]'); <doc:scenario>
* expect(n1.attr('className')).not().toMatch(/ng-validation-error/); it('should invalidate number', function(){
* input('n1').enter('1.x'); var n1 = element('.doc-example :input[name=n1]');
* expect(n1.attr('className')).toMatch(/ng-validation-error/); expect(n1.attr('className')).not().toMatch(/ng-validation-error/);
* input('n1').enter('1.x');
* var n2 = element('.doc-example :input[name=n2]'); expect(n1.attr('className')).toMatch(/ng-validation-error/);
* expect(n2.attr('className')).not().toMatch(/ng-validation-error/); var n2 = element('.doc-example :input[name=n2]');
* input('n2').enter('9'); expect(n2.attr('className')).not().toMatch(/ng-validation-error/);
* expect(n2.attr('className')).toMatch(/ng-validation-error/); input('n2').enter('9');
* expect(n2.attr('className')).toMatch(/ng-validation-error/);
* var n3 = element('.doc-example :input[name=n3]'); var n3 = element('.doc-example :input[name=n3]');
* expect(n3.attr('className')).not().toMatch(/ng-validation-error/); expect(n3.attr('className')).not().toMatch(/ng-validation-error/);
* input('n3').enter('201'); input('n3').enter('201');
* expect(n3.attr('className')).toMatch(/ng-validation-error/); expect(n3.attr('className')).toMatch(/ng-validation-error/);
* });
* }); </doc:scenario>
</doc:example>
* *
*/ */
'number': function(value, min, max) { 'number': function(value, min, max) {
@ -110,28 +114,29 @@ extend(angularValidator, {
* @css ng-validation-error * @css ng-validation-error
* *
* @example * @example
* Enter integer: <input name="n1" ng:validate="integer" > <br> <doc:example>
* Enter integer equal or greater than 10: <input name="n2" ng:validate="integer:10" > <br> <doc:source>
* Enter integer between 100 and 200 (inclusive): <input name="n3" ng:validate="integer:100:200" > <br> Enter integer: <input name="n1" ng:validate="integer" > <br>
* Enter integer equal or greater than 10: <input name="n2" ng:validate="integer:10" > <br>
* @scenario Enter integer between 100 and 200 (inclusive): <input name="n3" ng:validate="integer:100:200" > <br>
* it('should invalidate integer', function(){ </doc:source>
* var n1 = element('.doc-example :input[name=n1]'); <doc:scenario>
* expect(n1.attr('className')).not().toMatch(/ng-validation-error/); it('should invalidate integer', function(){
* input('n1').enter('1.1'); var n1 = element('.doc-example :input[name=n1]');
* expect(n1.attr('className')).toMatch(/ng-validation-error/); expect(n1.attr('className')).not().toMatch(/ng-validation-error/);
* input('n1').enter('1.1');
* var n2 = element('.doc-example :input[name=n2]'); expect(n1.attr('className')).toMatch(/ng-validation-error/);
* expect(n2.attr('className')).not().toMatch(/ng-validation-error/); var n2 = element('.doc-example :input[name=n2]');
* input('n2').enter('10.1'); expect(n2.attr('className')).not().toMatch(/ng-validation-error/);
* expect(n2.attr('className')).toMatch(/ng-validation-error/); input('n2').enter('10.1');
* expect(n2.attr('className')).toMatch(/ng-validation-error/);
* var n3 = element('.doc-example :input[name=n3]'); var n3 = element('.doc-example :input[name=n3]');
* expect(n3.attr('className')).not().toMatch(/ng-validation-error/); expect(n3.attr('className')).not().toMatch(/ng-validation-error/);
* input('n3').enter('100.1'); input('n3').enter('100.1');
* expect(n3.attr('className')).toMatch(/ng-validation-error/); expect(n3.attr('className')).toMatch(/ng-validation-error/);
* });
* }); </doc:scenario>
</doc:example>
*/ */
'integer': function(value, min, max) { 'integer': function(value, min, max) {
var numberError = angularValidator['number'](value, min, max); var numberError = angularValidator['number'](value, min, max);
@ -154,16 +159,20 @@ extend(angularValidator, {
* @css ng-validation-error * @css ng-validation-error
* *
* @example * @example
* Enter valid date: <doc:example>
* <input name="text" value="1/1/2009" ng:validate="date" > <doc:source>
* Enter valid date:
* @scenario <input name="text" value="1/1/2009" ng:validate="date" >
* it('should invalidate date', function(){ </doc:source>
* var n1 = element('.doc-example :input'); <doc:scenario>
* expect(n1.attr('className')).not().toMatch(/ng-validation-error/); it('should invalidate date', function(){
* input('text').enter('123/123/123'); var n1 = element('.doc-example :input');
* expect(n1.attr('className')).toMatch(/ng-validation-error/); expect(n1.attr('className')).not().toMatch(/ng-validation-error/);
* }); input('text').enter('123/123/123');
expect(n1.attr('className')).toMatch(/ng-validation-error/);
});
</doc:scenario>
</doc:example>
* *
*/ */
'date': function(value) { 'date': function(value) {
@ -187,16 +196,20 @@ extend(angularValidator, {
* @css ng-validation-error * @css ng-validation-error
* *
* @example * @example
* Enter valid email: <doc:example>
* <input name="text" ng:validate="email" value="me@example.com"> <doc:source>
* Enter valid email:
* @scenario <input name="text" ng:validate="email" value="me@example.com">
* it('should invalidate email', function(){ </doc:source>
* var n1 = element('.doc-example :input'); <doc:scenario>
* expect(n1.attr('className')).not().toMatch(/ng-validation-error/); it('should invalidate email', function(){
* input('text').enter('a@b.c'); var n1 = element('.doc-example :input');
* expect(n1.attr('className')).toMatch(/ng-validation-error/); expect(n1.attr('className')).not().toMatch(/ng-validation-error/);
* }); input('text').enter('a@b.c');
expect(n1.attr('className')).toMatch(/ng-validation-error/);
});
</doc:scenario>
</doc:example>
* *
*/ */
'email': function(value) { 'email': function(value) {
@ -217,16 +230,20 @@ extend(angularValidator, {
* @css ng-validation-error * @css ng-validation-error
* *
* @example * @example
* Enter valid phone number: <doc:example>
* <input name="text" value="1(234)567-8901" ng:validate="phone" > <doc:source>
* Enter valid phone number:
* @scenario <input name="text" value="1(234)567-8901" ng:validate="phone" >
* it('should invalidate phone', function(){ </doc:source>
* var n1 = element('.doc-example :input'); <doc:scenario>
* expect(n1.attr('className')).not().toMatch(/ng-validation-error/); it('should invalidate phone', function(){
* input('text').enter('+12345678'); var n1 = element('.doc-example :input');
* expect(n1.attr('className')).toMatch(/ng-validation-error/); expect(n1.attr('className')).not().toMatch(/ng-validation-error/);
* }); input('text').enter('+12345678');
expect(n1.attr('className')).toMatch(/ng-validation-error/);
});
</doc:scenario>
</doc:example>
* *
*/ */
'phone': function(value) { 'phone': function(value) {
@ -250,16 +267,20 @@ extend(angularValidator, {
* @css ng-validation-error * @css ng-validation-error
* *
* @example * @example
* Enter valid phone number: <doc:example>
* <input name="text" value="http://example.com/abc.html" size="40" ng:validate="url" > <doc:source>
* Enter valid phone number:
* @scenario <input name="text" value="http://example.com/abc.html" size="40" ng:validate="url" >
* it('should invalidate url', function(){ </doc:source>
* var n1 = element('.doc-example :input'); <doc:scenario>
* expect(n1.attr('className')).not().toMatch(/ng-validation-error/); it('should invalidate url', function(){
* input('text').enter('abc://server/path'); var n1 = element('.doc-example :input');
* expect(n1.attr('className')).toMatch(/ng-validation-error/); expect(n1.attr('className')).not().toMatch(/ng-validation-error/);
* }); input('text').enter('abc://server/path');
expect(n1.attr('className')).toMatch(/ng-validation-error/);
});
</doc:scenario>
</doc:example>
* *
*/ */
'url': function(value) { 'url': function(value) {
@ -280,17 +301,21 @@ extend(angularValidator, {
* @css ng-validation-error * @css ng-validation-error
* *
* @example * @example
* <textarea name="json" cols="60" rows="5" ng:validate="json"> <doc:example>
* {name:'abc'} <doc:source>
* </textarea> <textarea name="json" cols="60" rows="5" ng:validate="json">
* {name:'abc'}
* @scenario </textarea>
* it('should invalidate json', function(){ </doc:source>
* var n1 = element('.doc-example :input'); <doc:scenario>
* expect(n1.attr('className')).not().toMatch(/ng-validation-error/); it('should invalidate json', function(){
* input('json').enter('{name}'); var n1 = element('.doc-example :input');
* expect(n1.attr('className')).toMatch(/ng-validation-error/); expect(n1.attr('className')).not().toMatch(/ng-validation-error/);
* }); input('json').enter('{name}');
expect(n1.attr('className')).toMatch(/ng-validation-error/);
});
</doc:scenario>
</doc:example>
* *
*/ */
'json': function(value) { 'json': function(value) {
@ -338,35 +363,35 @@ extend(angularValidator, {
* @css ng-input-indicator-wait, ng-validation-error * @css ng-input-indicator-wait, ng-validation-error
* *
* @example * @example
* <script> <doc:example>
* function MyCntl(){ <doc:source>
* this.myValidator = function (inputToValidate, validationDone) { <script>
* setTimeout(function(){ function MyCntl(){
* validationDone(inputToValidate.length % 2); this.myValidator = function (inputToValidate, validationDone) {
* }, 500); setTimeout(function(){
* } validationDone(inputToValidate.length % 2);
* } }, 500);
* </script> }
* This input is validated asynchronously: }
* <div ng:controller="MyCntl"> </script>
* <input name="text" ng:validate="asynchronous:myValidator"> This input is validated asynchronously:
* </div> <div ng:controller="MyCntl">
* <input name="text" ng:validate="asynchronous:myValidator">
* @scenario </div>
* it('should change color in delayed way', function(){ </doc:source>
* var textBox = element('.doc-example :input'); <doc:scenario>
* expect(textBox.attr('className')).not().toMatch(/ng-input-indicator-wait/); it('should change color in delayed way', function(){
* expect(textBox.attr('className')).not().toMatch(/ng-validation-error/); var textBox = element('.doc-example :input');
* expect(textBox.attr('className')).not().toMatch(/ng-input-indicator-wait/);
* input('text').enter('X'); expect(textBox.attr('className')).not().toMatch(/ng-validation-error/);
* expect(textBox.attr('className')).toMatch(/ng-input-indicator-wait/); input('text').enter('X');
* expect(textBox.attr('className')).toMatch(/ng-input-indicator-wait/);
* pause(.6); pause(.6);
* expect(textBox.attr('className')).not().toMatch(/ng-input-indicator-wait/);
* expect(textBox.attr('className')).not().toMatch(/ng-input-indicator-wait/); expect(textBox.attr('className')).toMatch(/ng-validation-error/);
* expect(textBox.attr('className')).toMatch(/ng-validation-error/); });
* </doc:scenario>
* }); </doc:example>
* *
*/ */
/* /*

View file

@ -19,6 +19,8 @@
* </select> * </select>
* *
* @example * @example
<doc:example>
<doc:source>
<table style="font-size:.9em;"> <table style="font-size:.9em;">
<tr> <tr>
<th>Name</th> <th>Name</th>
@ -96,40 +98,43 @@
<td><tt>{{input6|json}}</tt></td> <td><tt>{{input6|json}}</tt></td>
</tr> </tr>
</table> </table>
</doc:source>
<doc:scenario>
* @scenario it('should exercise text', function(){
* it('should exercise text', function(){ input('input1').enter('Carlos');
* input('input1').enter('Carlos'); expect(binding('input1')).toEqual('"Carlos"');
* expect(binding('input1')).toEqual('"Carlos"'); });
* }); it('should exercise textarea', function(){
* it('should exercise textarea', function(){ input('input2').enter('Carlos');
* input('input2').enter('Carlos'); expect(binding('input2')).toEqual('"Carlos"');
* expect(binding('input2')).toEqual('"Carlos"'); });
* }); it('should exercise radio', function(){
* it('should exercise radio', function(){ expect(binding('input3')).toEqual('null');
* expect(binding('input3')).toEqual('null'); input('input3').select('A');
* input('input3').select('A'); expect(binding('input3')).toEqual('"A"');
* expect(binding('input3')).toEqual('"A"'); input('input3').select('B');
* input('input3').select('B'); expect(binding('input3')).toEqual('"B"');
* expect(binding('input3')).toEqual('"B"'); });
* }); it('should exercise checkbox', function(){
* it('should exercise checkbox', function(){ expect(binding('input4')).toEqual('false');
* expect(binding('input4')).toEqual('false'); input('input4').check();
* input('input4').check(); expect(binding('input4')).toEqual('true');
* expect(binding('input4')).toEqual('true'); });
* }); it('should exercise pulldown', function(){
* it('should exercise pulldown', function(){ expect(binding('input5')).toEqual('"c"');
* expect(binding('input5')).toEqual('"c"'); select('input5').option('d');
* select('input5').option('d'); expect(binding('input5')).toEqual('"d"');
* expect(binding('input5')).toEqual('"d"'); });
* }); it('should exercise multiselect', function(){
* it('should exercise multiselect', function(){ expect(binding('input6')).toEqual('[]');
* expect(binding('input6')).toEqual('[]'); select('input6').options('e');
* select('input6').options('e'); expect(binding('input6')).toEqual('["e"]');
* expect(binding('input6')).toEqual('["e"]'); select('input6').options('e', 'f');
* select('input6').options('e', 'f'); expect(binding('input6')).toEqual('["e","f"]');
* expect(binding('input6')).toEqual('["e","f"]'); });
* }); </doc:scenario>
</doc:example>
*/ */
function modelAccessor(scope, element) { function modelAccessor(scope, element) {
@ -193,18 +198,19 @@ function compileFormatter(expr) {
* @element INPUT * @element INPUT
* @css ng-validation-error * @css ng-validation-error
* *
* @exampleDescription * @example
* This example shows how the input element becomes red when it contains invalid input. Correct * This example shows how the input element becomes red when it contains invalid input. Correct
* the input to make the error disappear. * the input to make the error disappear.
* *
* @example <doc:example>
<doc:source>
I don't validate: I don't validate:
<input type="text" name="value" value="NotANumber"><br/> <input type="text" name="value" value="NotANumber"><br/>
I need an integer or nothing: I need an integer or nothing:
<input type="text" name="value" ng:validate="integer"><br/> <input type="text" name="value" ng:validate="integer"><br/>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:validate', function(){ it('should check ng:validate', function(){
expect(element('.doc-example-live :input:last').attr('className')). expect(element('.doc-example-live :input:last').attr('className')).
toMatch(/ng-validation-error/); toMatch(/ng-validation-error/);
@ -213,6 +219,8 @@ function compileFormatter(expr) {
expect(element('.doc-example-live :input:last').attr('className')). expect(element('.doc-example-live :input:last').attr('className')).
not().toMatch(/ng-validation-error/); not().toMatch(/ng-validation-error/);
}); });
</doc:scenario>
</doc:example>
*/ */
/** /**
* @workInProgress * @workInProgress
@ -226,19 +234,22 @@ function compileFormatter(expr) {
* @element INPUT * @element INPUT
* @css ng-validation-error * @css ng-validation-error
* *
* @exampleDescription * @example
* This example shows how the input element becomes red when it contains invalid input. Correct * This example shows how the input element becomes red when it contains invalid input. Correct
* the input to make the error disappear. * the input to make the error disappear.
* *
* @example <doc:example>
<doc:source>
I cannot be blank: <input type="text" name="value" ng:required><br/> I cannot be blank: <input type="text" name="value" ng:required><br/>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:required', function(){ it('should check ng:required', function(){
expect(element('.doc-example-live :input').attr('className')).toMatch(/ng-validation-error/); expect(element('.doc-example-live :input').attr('className')).toMatch(/ng-validation-error/);
input('value').enter('123'); input('value').enter('123');
expect(element('.doc-example-live :input').attr('className')).not().toMatch(/ng-validation-error/); expect(element('.doc-example-live :input').attr('className')).not().toMatch(/ng-validation-error/);
}); });
</doc:scenario>
</doc:example>
*/ */
/** /**
* @workInProgress * @workInProgress
@ -256,21 +267,24 @@ function compileFormatter(expr) {
* *
* @element INPUT * @element INPUT
* *
* @exampleDescription * @example
* This example shows how the user input is converted from a string and internally represented as an * This example shows how the user input is converted from a string and internally represented as an
* array. * array.
* *
* @example <doc:example>
<doc:source>
Enter a comma separated list of items: Enter a comma separated list of items:
<input type="text" name="list" ng:format="list" value="table, chairs, plate"> <input type="text" name="list" ng:format="list" value="table, chairs, plate">
<pre>list={{list}}</pre> <pre>list={{list}}</pre>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:format', function(){ it('should check ng:format', function(){
expect(binding('list')).toBe('list=["table","chairs","plate"]'); expect(binding('list')).toBe('list=["table","chairs","plate"]');
input('list').enter(',,, a ,,,'); input('list').enter(',,, a ,,,');
expect(binding('list')).toBe('list=["a"]'); expect(binding('list')).toBe('list=["a"]');
}); });
</doc:scenario>
</doc:example>
*/ */
function valueAccessor(scope, element) { function valueAccessor(scope, element) {
var validatorName = element.attr('ng:validate') || NOOP, var validatorName = element.attr('ng:validate') || NOOP,
@ -453,15 +467,17 @@ function radioInit(model, view, element) {
* @element INPUT * @element INPUT
* @param {expression} expression to execute. * @param {expression} expression to execute.
* *
* @exampleDescription
* @example * @example
* @example
<doc:example>
<doc:source>
<div ng:init="checkboxCount=0; textCount=0"></div> <div ng:init="checkboxCount=0; textCount=0"></div>
<input type="text" name="text" ng:change="textCount = 1 + textCount"> <input type="text" name="text" ng:change="textCount = 1 + textCount">
changeCount {{textCount}}<br/> changeCount {{textCount}}<br/>
<input type="checkbox" name="checkbox" ng:change="checkboxCount = 1 + checkboxCount"> <input type="checkbox" name="checkbox" ng:change="checkboxCount = 1 + checkboxCount">
changeCount {{checkboxCount}}<br/> changeCount {{checkboxCount}}<br/>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:change', function(){ it('should check ng:change', function(){
expect(binding('textCount')).toBe('0'); expect(binding('textCount')).toBe('0');
expect(binding('checkboxCount')).toBe('0'); expect(binding('checkboxCount')).toBe('0');
@ -475,6 +491,8 @@ function radioInit(model, view, element) {
expect(binding('textCount')).toBe('1'); expect(binding('textCount')).toBe('1');
expect(binding('checkboxCount')).toBe('1'); expect(binding('checkboxCount')).toBe('1');
}); });
</doc:scenario>
</doc:example>
*/ */
function inputWidget(events, modelAccessor, viewAccessor, initFn, textBox) { function inputWidget(events, modelAccessor, viewAccessor, initFn, textBox) {
return injectService(['$updateView', '$defer'], function($updateView, $defer, element) { return injectService(['$updateView', '$defer'], function($updateView, $defer, element) {
@ -594,27 +612,31 @@ angularWidget('option', function(){
* @param {string=} onload Expression to evaluate when a new partial is loaded. * @param {string=} onload Expression to evaluate when a new partial is loaded.
* *
* @example * @example
* <select name="url"> <doc:example>
* <option value="angular.filter.date.html">date filter</option> <doc:source>
* <option value="angular.filter.html.html">html filter</option> <select name="url">
* <option value="">(blank)</option> <option value="angular.filter.date.html">date filter</option>
* </select> <option value="angular.filter.html.html">html filter</option>
* <tt>url = <a href="{{url}}">{{url}}</a></tt> <option value="">(blank)</option>
* <hr/> </select>
* <ng:include src="url"></ng:include> <tt>url = <a href="{{url}}">{{url}}</a></tt>
* <hr/>
* @scenario <ng:include src="url"></ng:include>
* it('should load date filter', function(){ </doc:source>
* expect(element('.doc-example ng\\:include').text()).toMatch(/angular\.filter\.date/); <doc:scenario>
* }); it('should load date filter', function(){
* it('should change to hmtl filter', function(){ expect(element('.doc-example ng\\:include').text()).toMatch(/angular\.filter\.date/);
* select('url').option('angular.filter.html.html'); });
* expect(element('.doc-example ng\\:include').text()).toMatch(/angular\.filter\.html/); it('should change to hmtl filter', function(){
* }); select('url').option('angular.filter.html.html');
* it('should change to blank', function(){ expect(element('.doc-example ng\\:include').text()).toMatch(/angular\.filter\.html/);
* select('url').option('(blank)'); });
* expect(element('.doc-example ng\\:include').text()).toEqual(''); it('should change to blank', function(){
* }); select('url').option('(blank)');
expect(element('.doc-example ng\\:include').text()).toEqual('');
});
</doc:scenario>
</doc:example>
*/ */
angularWidget('ng:include', function(element){ angularWidget('ng:include', function(element){
var compiler = this, var compiler = this,
@ -690,6 +712,8 @@ angularWidget('ng:include', function(element){
* * `ng:switch-default`: the default case when no other casses match. * * `ng:switch-default`: the default case when no other casses match.
* *
* @example * @example
<doc:example>
<doc:source>
<select name="switch"> <select name="switch">
<option>settings</option> <option>settings</option>
<option>home</option> <option>home</option>
@ -703,19 +727,21 @@ angularWidget('ng:include', function(element){
<span ng:switch-default>default</span> <span ng:switch-default>default</span>
</ng:switch> </ng:switch>
</code> </code>
* </doc:source>
* @scenario <doc:scenario>
* it('should start in settings', function(){ it('should start in settings', function(){
* expect(element('.doc-example ng\\:switch').text()).toEqual('Settings Div'); expect(element('.doc-example ng\\:switch').text()).toEqual('Settings Div');
* }); });
* it('should change to home', function(){ it('should change to home', function(){
* select('switch').option('home'); select('switch').option('home');
* expect(element('.doc-example ng\\:switch').text()).toEqual('Home Span'); expect(element('.doc-example ng\\:switch').text()).toEqual('Home Span');
* }); });
* it('should select deafault', function(){ it('should select deafault', function(){
* select('switch').option('other'); select('switch').option('other');
* expect(element('.doc-example ng\\:switch').text()).toEqual('default'); expect(element('.doc-example ng\\:switch').text()).toEqual('default');
* }); });
</doc:scenario>
</doc:example>
*/ */
var ngSwitch = angularWidget('ng:switch', function (element){ var ngSwitch = angularWidget('ng:switch', function (element){
var compiler = this, var compiler = this,
@ -838,10 +864,11 @@ angularWidget('a', function() {
* *
* For example: `(name, age) in {'adam':10, 'amalie':12}`. * For example: `(name, age) in {'adam':10, 'amalie':12}`.
* *
* @exampleDescription * @example
* This example initializes the scope to a list of names and * This example initializes the scope to a list of names and
* than uses `ng:repeat` to display every person. * than uses `ng:repeat` to display every person.
* @example <doc:example>
<doc:source>
<div ng:init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]"> <div ng:init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]">
I have {{friends.length}} friends. They are: I have {{friends.length}} friends. They are:
<ul> <ul>
@ -850,13 +877,16 @@ angularWidget('a', function() {
</li> </li>
</ul> </ul>
</div> </div>
* @scenario </doc:source>
<doc:scenario>
it('should check ng:repeat', function(){ it('should check ng:repeat', function(){
var r = using('.doc-example-live').repeater('ul li'); var r = using('.doc-example-live').repeater('ul li');
expect(r.count()).toBe(2); expect(r.count()).toBe(2);
expect(r.row(0)).toEqual(["1","John","25"]); expect(r.row(0)).toEqual(["1","John","25"]);
expect(r.row(1)).toEqual(["2","Mary","28"]); expect(r.row(1)).toEqual(["2","Mary","28"]);
}); });
</doc:scenario>
</doc:example>
*/ */
angularWidget("@ng:repeat", function(expression, element){ angularWidget("@ng:repeat", function(expression, element){
element.removeAttr('ng:repeat'); element.removeAttr('ng:repeat');
@ -946,20 +976,24 @@ angularWidget("@ng:repeat", function(expression, element){
* *
* @element ANY * @element ANY
* *
* @exampleDescription * @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 siple binding (`{{}}`) is present, but the one
* wrapped in `ng:non-bindable` is left alone. * wrapped in `ng:non-bindable` is left alone.
* *
* @example * @example
<doc:example>
<doc:source>
<div>Normal: {{1 + 2}}</div> <div>Normal: {{1 + 2}}</div>
<div ng:non-bindable>Ignored: {{1 + 2}}</div> <div ng:non-bindable>Ignored: {{1 + 2}}</div>
* </doc:source>
* @scenario <doc:scenario>
it('should check ng:non-bindable', function(){ it('should check ng:non-bindable', function(){
expect(using('.doc-example-live').binding('1 + 2')).toBe('3'); expect(using('.doc-example-live').binding('1 + 2')).toBe('3');
expect(using('.doc-example-live').element('div:last').text()). expect(using('.doc-example-live').element('div:last').text()).
toMatch(/1 \+ 2/); toMatch(/1 \+ 2/);
}); });
</doc:scenario>
</doc:example>
*/ */
angularWidget("@ng:non-bindable", noop); angularWidget("@ng:non-bindable", noop);
@ -989,26 +1023,30 @@ angularWidget("@ng:non-bindable", noop);
* - doesn't require `$route` service to be available on the root scope * - doesn't require `$route` service to be available on the root scope
* *
* *
* # Example * @example
* Because of the nature of this widget, we can't include the usual live example for it. Instead <doc:example>
* following is a code snippet showing the typical usage: <doc:source>
*
<pre>
<script> <script>
angular.service('routeConfig', function($route) { function MyCtrl($route) {
$route.when('/foo', {controller: MyCtrl, template: 'foo.html'}); $route.when('/overview', {controller: OverviewCtrl, template: 'guide.overview.html'});
$route.when('/bar', {controller: MyCtrl, template: 'bar.html'}); $route.when('/bootstrap', {controller: BootstrapCtrl, template: 'guide.bootstrap.html'});
}, {$inject: ['$route'], $eager: true}); console.log(window.$route = $route);
};
MyCtrl.$inject = ['$route'];
function MyCtrl() {}; function BootstrapCtrl(){}
function OverviewCtrl(){}
</script> </script>
<div> <div ng:controller="MyCtrl">
<a href="#/foo">foo</a> | <a href="#/bar">bar</a> | <a href="#/undefined">undefined</a><br/> <a href="#/overview">overview</a> | <a href="#/bootstrap">bootstrap</a> | <a href="#/undefined">undefined</a><br/>
The view is included below: The view is included below:
<hr/> <hr/>
<ng:view></ng:view> <ng:view></ng:view>
</div> </div>
</pre> </doc:source>
<doc:scenario>
</doc:scenario>
</doc:example>
*/ */
angularWidget('ng:view', function(element) { angularWidget('ng:view', function(element) {
var compiler = this; var compiler = this;