mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-01-12 22:45:52 +08:00
complete rewrite of documentation generation
- romeved mustache.js - unified templates - improved testability of the code
This commit is contained in:
@@ -1,58 +0,0 @@
|
||||
<h1>{{name}}</h1>
|
||||
|
||||
{{#workInProgress}}
|
||||
<fieldset class="workInProgress">
|
||||
<legend>Work In Progress</legend>
|
||||
This page is currently being revised. It might be incomplete or contain inaccuracies.
|
||||
{{{workInProgress.description}}}
|
||||
</fieldset>
|
||||
{{/workInProgress}}
|
||||
|
||||
{{#deprecated}}
|
||||
<fieldset class="deprecated">
|
||||
<legend>Deprecated API</legend>
|
||||
{{deprecated}}
|
||||
</fieldset>
|
||||
{{/deprecated}}
|
||||
|
||||
<h2>Description</h2>
|
||||
{{{description}}}
|
||||
|
||||
<h2>Usage</h2>
|
||||
<h3>In HTML Template Binding</h3>
|
||||
<tt>
|
||||
<pre>
|
||||
<{{element}} {{shortName}}="{{paramFirst.name}}">
|
||||
...
|
||||
</{{element}}>
|
||||
</pre>
|
||||
</tt>
|
||||
|
||||
<h3>Parameters</h3>
|
||||
<ul>
|
||||
{{#param}}
|
||||
<li><tt>{{name}}</tt> –
|
||||
<tt>{{{#type}}{{type}}{{/type}}{{^type}}*{{/type}}{{#optional}}={{/optional}}}</tt>
|
||||
<tt>{{#default}}[{{default}}]{{/default}}</tt>
|
||||
– {{{description}}}</li>
|
||||
{{/param}}
|
||||
</ul>
|
||||
{{{paramDescription}}}
|
||||
|
||||
{{#css}}
|
||||
<h3>CSS</h3>
|
||||
{{{css}}}
|
||||
{{/css}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
<doc:scenario>{{{scenario}}}</doc:scenario>
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -1 +0,0 @@
|
||||
NG_PAGES={{{JSON}}};
|
||||
@@ -1,65 +0,0 @@
|
||||
<h1>{{name}}</h1>
|
||||
|
||||
{{#workInProgress}}
|
||||
<fieldset class="workInProgress">
|
||||
<legend>Work In Progress</legend>
|
||||
This page is currently being revised. It might be incomplete or contain inaccuracies.
|
||||
{{{workInProgress.description}}}
|
||||
</fieldset>
|
||||
{{/workInProgress}}
|
||||
|
||||
{{#deprecated}}
|
||||
<fieldset class="deprecated">
|
||||
<legend>Deprecated API</legend>
|
||||
{{deprecated}}
|
||||
</fieldset>
|
||||
{{/deprecated}}
|
||||
|
||||
<h2>Description</h2>
|
||||
{{{description}}}
|
||||
|
||||
<h2>Usage</h2>
|
||||
<h3>In HTML Template Binding</h3>
|
||||
<tt>
|
||||
<span>{{</span>
|
||||
{{paramFirst.name}}_expression
|
||||
| {{shortName}}{{#paramRest}}{{^default}}:{{name}}{{/default}}{{#default}}<i>[:{{name}}={{default}}]</i>{{/default}}{{/paramRest}}
|
||||
<span> }}</span>
|
||||
</tt>
|
||||
<h3>In JavaScript</h3>
|
||||
<tt ng:non-bindable>
|
||||
angular.filter.{{shortName}}({{paramFirst.name}}{{#paramRest}}, {{name}}{{/paramRest}} );
|
||||
</tt>
|
||||
|
||||
<h3>Parameters</h3>
|
||||
<ul>
|
||||
{{#param}}
|
||||
<li><tt>{{name}}</tt> –
|
||||
<tt>{{{#type}}{{type}}{{/type}}{{^type}}*{{/type}}{{#optional}}={{/optional}}}</tt>
|
||||
<tt>{{#default}}[{{default}}]{{/default}}</tt>
|
||||
– {{{description}}}</li>
|
||||
{{/param}}
|
||||
</ul>
|
||||
|
||||
{{#returns}}
|
||||
<h3>Returns</h3>
|
||||
<tt>{{{{type}}}}</tt> {{{description}}}
|
||||
{{/returns}}
|
||||
|
||||
{{#css}}
|
||||
<h3>CSS</h3>
|
||||
{{{css}}}
|
||||
{{/css}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
<doc:scenario>{{{scenario}}}</doc:scenario>
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -1,53 +0,0 @@
|
||||
<h1>{{name}}</h1>
|
||||
|
||||
{{#workInProgress}}
|
||||
<fieldset class="workInProgress">
|
||||
<legend>Work In Progress</legend>
|
||||
This page is currently being revised. It might be incomplete or contain inaccuracies.
|
||||
{{{workInProgress.description}}}
|
||||
</fieldset>
|
||||
{{/workInProgress}}
|
||||
|
||||
{{#deprecated}}
|
||||
<fieldset class="deprecated">
|
||||
<legend>Deprecated API</legend>
|
||||
{{deprecated}}
|
||||
</fieldset>
|
||||
{{/deprecated}}
|
||||
|
||||
<h2>Description</h2>
|
||||
{{{description}}}
|
||||
|
||||
<h2>Usage</h2>
|
||||
<h3>In HTML Template Binding</h3>
|
||||
<tt>
|
||||
<input type="text" ng:format="{{shortName}}">
|
||||
</tt>
|
||||
<h3>In JavaScript</h3>
|
||||
<tt ng:non-bindable>
|
||||
var userInputString = angular.formatter.{{shortName}}.format(modelValue);<br/>
|
||||
var modelValue = angular.formatter.{{shortName}}.parse(userInputString);
|
||||
</tt>
|
||||
|
||||
{{#returns}}
|
||||
<h3>Returns</h3>
|
||||
<tt>{{{{type}}}}</tt> {{{description}}}
|
||||
{{/returns}}
|
||||
|
||||
{{#css}}
|
||||
<h3>CSS</h3>
|
||||
{{{css}}}
|
||||
{{/css}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
<doc:scenario>{{{scenario}}}</doc:scenario>
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -1,52 +0,0 @@
|
||||
<h1>{{name}}</h1>
|
||||
|
||||
{{#workInProgress}}
|
||||
<fieldset class="workInProgress">
|
||||
<legend>Work In Progress</legend>
|
||||
This page is currently being revised. It might be incomplete or contain inaccuracies.
|
||||
{{{workInProgress.description}}}
|
||||
</fieldset>
|
||||
{{/workInProgress}}
|
||||
|
||||
{{#deprecated}}
|
||||
<fieldset class="deprecated">
|
||||
<legend>Deprecated API</legend>
|
||||
{{deprecated}}
|
||||
</fieldset>
|
||||
{{/deprecated}}
|
||||
|
||||
<h2>Description</h2>
|
||||
{{{description}}}
|
||||
|
||||
<h2>Usage</h2>
|
||||
<tt ng:non-bindable>
|
||||
{{name}}({{paramFirst.name}}{{#paramRest}}, {{name}}{{/paramRest}} );
|
||||
</tt>
|
||||
|
||||
<h3>Parameters</h3>
|
||||
<ul>
|
||||
{{#param}}
|
||||
<li><tt>{{name}}</tt> –
|
||||
<tt>{{{#type}}{{type}}{{/type}}{{^type}}*{{/type}}{{#optional}}={{/optional}}}</tt>
|
||||
<tt>{{#default}}[{{default}}]{{/default}}</tt>
|
||||
– {{{description}}}</li>
|
||||
{{/param}}
|
||||
</ul>
|
||||
|
||||
{{#returns}}
|
||||
<h3>Returns</h3>
|
||||
<tt>{{{{type}}}}</tt> {{{description}}}
|
||||
{{/returns}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
<doc:scenario>{{{scenario}}}</doc:scenario>
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -1,31 +0,0 @@
|
||||
<h1>{{name}}</h1>
|
||||
|
||||
{{#workInProgress}}
|
||||
<fieldset class="workInProgress">
|
||||
<legend>Work In Progress</legend>
|
||||
This page is currently being revised. It might be incomplete or contain inaccuracies.
|
||||
{{{workInProgress.description}}}
|
||||
</fieldset>
|
||||
{{/workInProgress}}
|
||||
|
||||
{{#deprecated}}
|
||||
<fieldset class="deprecated">
|
||||
<legend>Deprecated API</legend>
|
||||
{{deprecated}}
|
||||
</fieldset>
|
||||
{{/deprecated}}
|
||||
|
||||
{{{description}}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
<doc:scenario>{{{scenario}}}</doc:scenario>
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -1,288 +0,0 @@
|
||||
console.log(__dirname);
|
||||
require.paths.push(__dirname + "/../");
|
||||
require.paths.push(__dirname + "/../../");
|
||||
var fs = require('fs');
|
||||
var Script = process.binding('evals').Script;
|
||||
var collect = load('docs/collect.js');
|
||||
|
||||
describe('collect', function(){
|
||||
describe('markdown', function(){
|
||||
it('should replace angular in markdown', function(){
|
||||
expect(collect.markdown('<angular/>')).
|
||||
toEqual('<p><tt><angular/></tt></p>');
|
||||
});
|
||||
|
||||
it('should not replace anything in <pre>', function(){
|
||||
expect(collect.markdown('bah x\n<pre>\nangular.k\n</pre>\n asdf x')).
|
||||
toEqual(
|
||||
'<p>bah x</p>' +
|
||||
'<pre>\nangular.k\n</pre>' +
|
||||
'<p>asdf x</p>');
|
||||
});
|
||||
|
||||
it('should replace text between two <pre></pre> tags', function() {
|
||||
expect(collect.markdown('<pre>x</pre># One<pre>b</pre>')).
|
||||
toEqual('<pre>x</pre><h1>One</h1><pre>b</pre>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('processNgDoc', function() {
|
||||
var processNgDoc = collect.processNgDoc,
|
||||
documentation;
|
||||
|
||||
beforeEach(function() {
|
||||
documentation = {
|
||||
pages: [],
|
||||
byName: {}
|
||||
};
|
||||
});
|
||||
|
||||
it('should store references to docs by name', function() {
|
||||
var doc = {ngdoc: 'section', name: 'fake', raw: {text:''}};
|
||||
processNgDoc(documentation, doc);
|
||||
expect(documentation.byName.fake).toBe(doc);
|
||||
});
|
||||
|
||||
it('should connect doc to owner (specified by @methodOf)', function() {
|
||||
var parentDoc = {ngdoc: 'section', name: 'parent', raw: {text:''}};
|
||||
var doc = {ngdoc: 'section', name: 'child', methodOf: 'parent', raw: {text:''}};
|
||||
processNgDoc(documentation, parentDoc);
|
||||
processNgDoc(documentation, doc);
|
||||
expect(documentation.byName.parent.method).toBeDefined();
|
||||
expect(documentation.byName.parent.method[0]).toBe(doc);
|
||||
});
|
||||
|
||||
it('should not add doc to sections if @memberOf specified', function() {
|
||||
var parentDoc = {ngdoc: 'parent', name: 'parent', raw: {text:''}};
|
||||
var doc = {ngdoc: 'child', name: 'child', methodOf: 'parent', raw: {text:''}};
|
||||
processNgDoc(documentation, parentDoc);
|
||||
processNgDoc(documentation, doc);
|
||||
expect(documentation.pages.child).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should throw exception if owner does not exist', function() {
|
||||
expect(function() {
|
||||
processNgDoc(documentation, {ngdoc: 'section', methodOf: 'not.exist', raw: {text:''}});
|
||||
}).toThrow('Owner "not.exist" is not defined.');
|
||||
});
|
||||
|
||||
it('should ignore non-ng docs', function() {
|
||||
var doc = {name: 'anything'};
|
||||
expect(function() {
|
||||
processNgDoc(documentation, doc);
|
||||
}).not.toThrow();
|
||||
expect(documentation.pages).not.toContain(doc);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TAG', function(){
|
||||
var TAG = collect.TAG;
|
||||
var doc;
|
||||
beforeEach(function(){
|
||||
doc = {};
|
||||
});
|
||||
|
||||
describe('@param', function(){
|
||||
it('should parse with no default', function(){
|
||||
TAG.param(doc, 'param',
|
||||
'{(number|string)} number Number \n to format.');
|
||||
expect(doc.param).toEqual([{
|
||||
type : '(number|string)',
|
||||
name : 'number',
|
||||
optional: false,
|
||||
'default' : undefined,
|
||||
description : 'Number \n to format.' }]);
|
||||
});
|
||||
it('should parse with default and optional', function(){
|
||||
TAG.param(doc, 'param',
|
||||
'{(number|string)=} [fractionSize=2] desc');
|
||||
expect(doc.param).toEqual([{
|
||||
type : '(number|string)',
|
||||
name : 'fractionSize',
|
||||
optional: true,
|
||||
'default' : '2',
|
||||
description : 'desc' }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('@requires', function() {
|
||||
it('should parse more @requires tag into array', function() {
|
||||
TAG.requires(doc, 'requires', '$service');
|
||||
TAG.requires(doc, 'requires', '$another');
|
||||
|
||||
expect(doc.requires).toEqual([
|
||||
{name: '$service'},
|
||||
{name: '$another'}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('@property', function() {
|
||||
it('should parse @property tags into array', function() {
|
||||
TAG.property(doc, 'property', '{type} name1 desc');
|
||||
TAG.property(doc, 'property', '{type} name2 desc');
|
||||
expect(doc.property.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should parse @property with only name', function() {
|
||||
TAG.property(doc, 'property', 'fake');
|
||||
expect(doc.property[0].name).toEqual('fake');
|
||||
});
|
||||
|
||||
it('should parse @property with optional type', function() {
|
||||
TAG.property(doc, 'property', '{string} name');
|
||||
expect(doc.property[0].name).toEqual('name');
|
||||
expect(doc.property[0].type).toEqual('string');
|
||||
});
|
||||
|
||||
it('should parse @property with optional description', function() {
|
||||
TAG.property(doc, 'property', 'name desc rip tion');
|
||||
expect(doc.property[0].name).toEqual('name');
|
||||
expect(doc.property[0].description).toEqual('desc rip tion');
|
||||
});
|
||||
|
||||
it('should parse @property with type and description both', function() {
|
||||
TAG.property(doc, 'property', '{bool} name desc rip tion');
|
||||
expect(doc.property[0].name).toEqual('name');
|
||||
expect(doc.property[0].type).toEqual('bool');
|
||||
expect(doc.property[0].description).toEqual('desc rip tion');
|
||||
});
|
||||
|
||||
/**
|
||||
* If property description is undefined, this variable is not set in the template,
|
||||
* so the whole @description tag is used instead
|
||||
*/
|
||||
it('should set undefined description to "false"', function() {
|
||||
TAG.property(doc, 'property', 'name');
|
||||
expect(doc.property[0].description).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('@methodOf', function() {
|
||||
it('should parse @methodOf tag', function() {
|
||||
expect(function() {
|
||||
TAG.methodOf(doc, 'methodOf', 'parentName');
|
||||
}).not.toThrow();
|
||||
expect(doc.methodOf).toEqual('parentName');
|
||||
});
|
||||
});
|
||||
|
||||
describe('@returns', function() {
|
||||
it('should not parse @returns without type', function() {
|
||||
expect(function() {TAG.returns(doc, 'returns', 'lala');})
|
||||
.toThrow();
|
||||
});
|
||||
|
||||
it('should parse @returns with type and description', function() {
|
||||
TAG.returns(doc, 'returns', '{string} descrip tion');
|
||||
expect(doc.returns).toEqual({type: 'string', description: 'descrip tion'});
|
||||
});
|
||||
|
||||
it('should transform description of @returns with markdown', function() {
|
||||
TAG.returns(doc, 'returns', '{string} descrip *tion*');
|
||||
expect(doc.returns).toEqual({type: 'string', description: 'descrip <em>tion</em>'});
|
||||
});
|
||||
|
||||
it('should support multiline content', function() {
|
||||
TAG.returns(doc, 'returns', '{string} description\n new line\n another line');
|
||||
expect(doc.returns).
|
||||
toEqual({type: 'string', description: 'description\n new line\n another line'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('@description', function(){
|
||||
it('should support pre blocks', function(){
|
||||
TAG.description(doc, 'description', '<pre>abc</pre>');
|
||||
expect(doc.description).
|
||||
toBe('<div ng:non-bindable><pre class="brush: js; html-script: true;">abc</pre></div>');
|
||||
});
|
||||
|
||||
it('should support multiple pre blocks', function() {
|
||||
TAG.description(doc, 'description', 'foo \n<pre>abc</pre>\n#bah\nfoo \n<pre>cba</pre>');
|
||||
expect(doc.description).
|
||||
toBe('<p>foo </p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js; html-script: true;">abc</pre></div>' +
|
||||
'<h2>bah</h2>\n\n' +
|
||||
'<p>foo </p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js; html-script: true;">cba</pre></div>');
|
||||
|
||||
});
|
||||
|
||||
it('should support nested @link annotations with or without description', function() {
|
||||
TAG.description(doc, 'description',
|
||||
'foo {@link angular.foo}\n\n da {@link angular.foo bar foo bar } \n\n' +
|
||||
'dad{@link angular.foo}\n\n' +
|
||||
'{@link angular.directive.ng:foo ng:foo}');
|
||||
expect(doc.description).
|
||||
toBe('<p>foo <a href="#!angular.foo"><code>angular.foo</code></a></p>\n\n' +
|
||||
'<p>da <a href="#!angular.foo"><code>bar foo bar</code></a> </p>\n\n' +
|
||||
'<p>dad<a href="#!angular.foo"><code>angular.foo</code></a></p>\n\n' +
|
||||
'<p><a href="#!angular.directive.ng:foo"><code>ng:foo</code></a></p>');
|
||||
});
|
||||
|
||||
it('should increment all headings by one', function() {
|
||||
TAG.description(doc, 'description', '# foo\nabc');
|
||||
expect(doc.description).
|
||||
toBe('<h2>foo</h2>\n\n<p>abc</p>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('@example', function(){
|
||||
it('should not remove {{}}', function(){
|
||||
TAG.example(doc, 'example', 'text {{ abc }}');
|
||||
expect(doc.example).toEqual('text {{ abc }}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('@deprecated', function() {
|
||||
it('should parse @deprecated', function() {
|
||||
TAG.deprecated(doc, 'deprecated', 'Replaced with foo.');
|
||||
expect(doc.deprecated).toBe('Replaced with foo.');
|
||||
})
|
||||
});
|
||||
|
||||
describe('@workInProgress', function() {
|
||||
it('should parse @workInProgress without a description and default to true', function() {
|
||||
TAG.workInProgress(doc, 'workInProgress', '');
|
||||
expect(doc.workInProgress).toEqual({description: ''});
|
||||
});
|
||||
|
||||
it('should parse @workInProgress with a description', function() {
|
||||
TAG.workInProgress(doc, 'workInProgress', 'my description');
|
||||
expect(doc.workInProgress).toEqual({description: '<p>my description</p>'});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('trim', function(){
|
||||
var trim = collect.trim;
|
||||
it('should remove leading/trailing space', function(){
|
||||
expect(trim(' \nabc\n ')).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should remove leading space on every line', function(){
|
||||
expect(trim('\n 1\n 2\n 3\n')).toEqual('1\n 2\n 3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('keywords', function(){
|
||||
var keywords = collect.keywords;
|
||||
it('should collect keywords', function(){
|
||||
expect(keywords('\nHello: World! @ignore.')).toEqual('hello world');
|
||||
expect(keywords('The `ng:class-odd` and ')).toEqual('and ng:class-odd the');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function load(path){
|
||||
var sandbox = {
|
||||
require: require,
|
||||
console: console,
|
||||
__dirname: __dirname,
|
||||
testmode: true
|
||||
};
|
||||
Script.runInNewContext(fs.readFileSync(path), sandbox, path);
|
||||
return sandbox;
|
||||
}
|
||||
257
docs/spec/ngdocSpec.js
Normal file
257
docs/spec/ngdocSpec.js
Normal file
@@ -0,0 +1,257 @@
|
||||
var ngdoc = require('ngdoc.js');
|
||||
|
||||
describe('ngdoc', function(){
|
||||
var Doc = ngdoc.Doc;
|
||||
describe('Doc', function(){
|
||||
describe('metadata', function(){
|
||||
|
||||
it('should find keywords', function(){
|
||||
expect(new Doc('\nHello: World! @ignore.').keywords()).toEqual('hello world');
|
||||
expect(new Doc('The `ng:class-odd` and').keywords()).toEqual('and ng:class-odd the');
|
||||
});
|
||||
});
|
||||
|
||||
describe('parse', function(){
|
||||
it('should convert @names into properties', function(){
|
||||
var doc = new Doc('\n@name name\n@desc\ndesc\ndesc2\n@dep\n');
|
||||
doc.parse();
|
||||
expect(doc.name).toEqual('name');
|
||||
expect(doc.desc).toEqual('desc\ndesc2');
|
||||
expect(doc.dep).toEqual('');
|
||||
});
|
||||
|
||||
it('should parse parameters', function(){
|
||||
var doc = new Doc(
|
||||
'@param {*} a short\n' +
|
||||
'@param {Type} b med\n' +
|
||||
'@param {Class=} [c=2] long\nline');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([
|
||||
{name:'a', description:'short', type:'*', optional:false, 'default':undefined},
|
||||
{name:'b', description:'med', type:'Type', optional:false, 'default':undefined},
|
||||
{name:'c', description:'long\nline', type:'Class', optional:true, 'default':'2'}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should parse return', function(){
|
||||
var doc = new Doc('@returns {Type} text *bold*.');
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({
|
||||
type: 'Type',
|
||||
description: 'text <em>bold</em>.'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('markdown', function(){
|
||||
var markdown = ngdoc.markdown;
|
||||
|
||||
it('should replace angular in markdown', function(){
|
||||
expect(markdown('<angular/>')).
|
||||
toEqual('<p><tt><angular/></tt></p>');
|
||||
});
|
||||
|
||||
it('should not replace anything in <pre>', function(){
|
||||
expect(markdown('bah x\n<pre>\nangular.k\n</pre>\n asdf x')).
|
||||
toEqual(
|
||||
'<p>bah x</p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js; html-script: true;">\n' +
|
||||
'angular.k\n' +
|
||||
'</pre></div>' +
|
||||
'<p>asdf x</p>');
|
||||
});
|
||||
|
||||
it('should replace text between two <pre></pre> tags', function() {
|
||||
expect(markdown('<pre>x</pre># One<pre>b</pre>')).
|
||||
toMatch('</div><h3>One</h3><div');
|
||||
});
|
||||
});
|
||||
|
||||
describe('trim', function(){
|
||||
var trim = ngdoc.trim;
|
||||
it('should remove leading/trailing space', function(){
|
||||
expect(trim(' \nabc\n ')).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should remove leading space on every line', function(){
|
||||
expect(trim('\n 1\n 2\n 3\n')).toEqual('1\n 2\n 3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('merge', function(){
|
||||
it('should merge child with parent', function(){
|
||||
var parent = new Doc({name:'angular.service.abc'});
|
||||
var methodA = new Doc({name:'methodA', methodOf:'angular.service.abc'});
|
||||
var methodB = new Doc({name:'methodB', methodOf:'angular.service.abc'});
|
||||
var propA = new Doc({name:'propA', propertyOf:'angular.service.abc'});
|
||||
var propB = new Doc({name:'propB', propertyOf:'angular.service.abc'});
|
||||
;var docs = [methodB, methodA, propB, propA, parent]; // keep wrong order;
|
||||
ngdoc.merge(docs);
|
||||
expect(docs.length).toEqual(1);
|
||||
expect(docs[0].name).toEqual('angular.service.abc');
|
||||
expect(docs[0].methods).toEqual([methodA, methodB]);
|
||||
expect(docs[0].properties).toEqual([propA, propB]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
describe('TAG', function(){
|
||||
describe('@param', function(){
|
||||
it('should parse with no default', function(){
|
||||
var doc = new Doc('@param {(number|string)} number Number \n to format.');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([{
|
||||
type : '(number|string)',
|
||||
name : 'number',
|
||||
optional: false,
|
||||
'default' : undefined,
|
||||
description : 'Number \n to format.' }]);
|
||||
});
|
||||
|
||||
it('should parse with default and optional', function(){
|
||||
var doc = new Doc('@param {(number|string)=} [fractionSize=2] desc');
|
||||
doc.parse();
|
||||
expect(doc.param).toEqual([{
|
||||
type : '(number|string)',
|
||||
name : 'fractionSize',
|
||||
optional: true,
|
||||
'default' : '2',
|
||||
description : 'desc' }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('@requires', function() {
|
||||
it('should parse more @requires tag into array', function() {
|
||||
var doc = new Doc('@requires $service\n@requires $another');
|
||||
doc.parse();
|
||||
expect(doc.requires).toEqual(['$service', '$another']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('@property', function() {
|
||||
it('should parse @property tags into array', function() {
|
||||
var doc = new Doc("@property {type} name1 desc\n@property {type} name2 desc");
|
||||
doc.parse();
|
||||
expect(doc.properties.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should parse @property with only name', function() {
|
||||
var doc = new Doc("@property fake");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('fake');
|
||||
});
|
||||
|
||||
it('should parse @property with optional type', function() {
|
||||
var doc = new Doc("@property {string} name");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('name');
|
||||
expect(doc.properties[0].type).toEqual('string');
|
||||
});
|
||||
|
||||
it('should parse @property with optional description', function() {
|
||||
var doc = new Doc("@property name desc rip tion");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('name');
|
||||
expect(doc.properties[0].description).toEqual('desc rip tion');
|
||||
});
|
||||
|
||||
it('should parse @property with type and description both', function() {
|
||||
var doc = new Doc("@property {bool} name desc rip tion");
|
||||
doc.parse();
|
||||
expect(doc.properties[0].name).toEqual('name');
|
||||
expect(doc.properties[0].type).toEqual('bool');
|
||||
expect(doc.properties[0].description).toEqual('desc rip tion');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('@returns', function() {
|
||||
it('should not parse @returns without type', function() {
|
||||
var doc = new Doc("@returns lala");
|
||||
expect(doc.parse).toThrow();
|
||||
});
|
||||
|
||||
it('should parse @returns with type and description', function() {
|
||||
var doc = new Doc("@returns {string} descrip tion");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'string', description: 'descrip tion'});
|
||||
});
|
||||
|
||||
it('should transform description of @returns with markdown', function() {
|
||||
var doc = new Doc("@returns {string} descrip *tion*");
|
||||
doc.parse();
|
||||
expect(doc.returns).toEqual({type: 'string', description: 'descrip <em>tion</em>'});
|
||||
});
|
||||
|
||||
it('should support multiline content', function() {
|
||||
var doc = new Doc("@returns {string} description\n new line\n another line");
|
||||
doc.parse();
|
||||
expect(doc.returns).
|
||||
toEqual({type: 'string', description: 'description\n new line\n another line'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('@description', function(){
|
||||
it('should support pre blocks', function(){
|
||||
var doc = new Doc("@description <pre>abc</pre>");
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<div ng:non-bindable><pre class="brush: js; html-script: true;">abc</pre></div>');
|
||||
});
|
||||
|
||||
it('should support multiple pre blocks', function() {
|
||||
var doc = new Doc("@description foo \n<pre>abc</pre>\n#bah\nfoo \n<pre>cba</pre>");
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<p>foo </p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js; html-script: true;">abc</pre></div>' +
|
||||
'<h3>bah</h3>\n\n' +
|
||||
'<p>foo </p>' +
|
||||
'<div ng:non-bindable><pre class="brush: js; html-script: true;">cba</pre></div>');
|
||||
|
||||
});
|
||||
|
||||
it('should support nested @link annotations with or without description', function() {
|
||||
var doc = new Doc("@description " +
|
||||
'foo {@link angular.foo}\n\n da {@link angular.foo bar foo bar } \n\n' +
|
||||
'dad{@link angular.foo}\n\n' +
|
||||
'{@link angular.directive.ng:foo ng:foo}');
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<p>foo <a href="#!angular.foo"><code>angular.foo</code></a></p>\n\n' +
|
||||
'<p>da <a href="#!angular.foo"><code>bar foo bar</code></a> </p>\n\n' +
|
||||
'<p>dad<a href="#!angular.foo"><code>angular.foo</code></a></p>\n\n' +
|
||||
'<p><a href="#!angular.directive.ng:foo"><code>ng:foo</code></a></p>');
|
||||
});
|
||||
|
||||
it('should increment all headings by two', function() {
|
||||
var doc = new Doc('@description # foo\nabc\n## bar \n xyz');
|
||||
doc.parse();
|
||||
expect(doc.description).
|
||||
toBe('<h3>foo</h3>\n\n<p>abc</p>\n\n<h4>bar</h4>\n\n<p>xyz</p>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('@example', function(){
|
||||
it('should not remove {{}}', function(){
|
||||
var doc = new Doc('@example text {{ abc }}');
|
||||
doc.parse();
|
||||
expect(doc.example).toEqual('text {{ abc }}');
|
||||
});
|
||||
});
|
||||
|
||||
describe('@deprecated', function() {
|
||||
it('should parse @deprecated', function() {
|
||||
var doc = new Doc('@deprecated Replaced with foo.');
|
||||
doc.parse();
|
||||
expect(doc.deprecated).toBe('Replaced with foo.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
39
docs/spec/specs.js
Normal file
39
docs/spec/specs.js
Normal file
@@ -0,0 +1,39 @@
|
||||
if (global.jasmine) return;
|
||||
|
||||
require.paths.push(__dirname + "/../../lib");
|
||||
require.paths.push(__dirname + '/../src');
|
||||
var jasmine = require('jasmine-1.0.1');
|
||||
var sys = require('util');
|
||||
|
||||
for(var key in jasmine) {
|
||||
global[key] = jasmine[key];
|
||||
}
|
||||
|
||||
//Patch Jasmine for proper stack traces
|
||||
jasmine.Spec.prototype.fail = function (e) {
|
||||
var expectationResult = new jasmine.ExpectationResult({
|
||||
passed: false,
|
||||
message: e ? jasmine.util.formatException(e) : 'Exception'
|
||||
});
|
||||
// PATCH
|
||||
if (e) {
|
||||
expectationResult.trace = e;
|
||||
}
|
||||
this.results_.addResult(expectationResult);
|
||||
};
|
||||
|
||||
|
||||
|
||||
var isVerbose = false;
|
||||
var showColors = true;
|
||||
process.argv.forEach(function(arg){
|
||||
switch(arg) {
|
||||
case '--color': showColors = true; break;
|
||||
case '--noColor': showColors = false; break;
|
||||
case '--verbose': isVerbose = true; break;
|
||||
}
|
||||
});
|
||||
|
||||
jasmine.executeSpecsInFolder(__dirname, function(runner, log){
|
||||
process.exit(runner.results().failedCount);
|
||||
}, isVerbose, showColors);
|
||||
18
docs/spec/writerSpec.js
Normal file
18
docs/spec/writerSpec.js
Normal file
@@ -0,0 +1,18 @@
|
||||
var writer = require('writer.js');
|
||||
describe('writer', function(){
|
||||
describe('toString', function(){
|
||||
var toString = writer.toString;
|
||||
|
||||
it('should merge string', function(){
|
||||
expect(toString('abc')).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should merge obj', function(){
|
||||
expect(toString({a:1})).toEqual('{"a":1}');
|
||||
});
|
||||
|
||||
it('should merge array', function(){
|
||||
expect(toString(['abc',{}])).toEqual('abc{}');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
require.paths.push("./lib");
|
||||
var jasmine = require('jasmine-1.0.1');
|
||||
var sys = require('util');
|
||||
|
||||
for(var key in jasmine) {
|
||||
global[key] = jasmine[key];
|
||||
}
|
||||
|
||||
var isVerbose = false;
|
||||
var showColors = true;
|
||||
process.argv.forEach(function(arg){
|
||||
switch(arg) {
|
||||
case '--color': showColors = true; break;
|
||||
case '--noColor': showColors = false; break;
|
||||
case '--verbose': isVerbose = true; break;
|
||||
}
|
||||
});
|
||||
|
||||
jasmine.executeSpecsInFolder(__dirname + '/spec', function(runner, log){
|
||||
process.exit(runner.results().failedCount);
|
||||
}, isVerbose, showColors);
|
||||
@@ -2,7 +2,10 @@ function noop(){}
|
||||
|
||||
function chain(delegateFn, explicitDone){
|
||||
var onDoneFn = noop;
|
||||
var onErrorFn = noop;
|
||||
var onErrorFn = function(e){
|
||||
console.error(e.stack || e);
|
||||
process.exit(-1);
|
||||
};
|
||||
var waitForCount = 1;
|
||||
delegateFn = delegateFn || noop;
|
||||
var stackError = new Error('capture stack');
|
||||
123
docs/src/dom.js
Normal file
123
docs/src/dom.js
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* DOM generation class
|
||||
*/
|
||||
|
||||
exports.DOM = DOM;
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
function DOM(){
|
||||
this.out = [];
|
||||
this.headingDepth = 1;
|
||||
}
|
||||
|
||||
var INLINE_TAGS = {
|
||||
i: true,
|
||||
b: true
|
||||
};
|
||||
|
||||
DOM.prototype = {
|
||||
toString: function() {
|
||||
return this.out.join('');
|
||||
},
|
||||
|
||||
text: function(content) {
|
||||
if (typeof content == "string") {
|
||||
this.out.push(content.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'));
|
||||
} else if (typeof content == 'function') {
|
||||
content.call(this, this);
|
||||
} else if (content instanceof Array) {
|
||||
this.ul(content);
|
||||
}
|
||||
},
|
||||
|
||||
html: function(html) {
|
||||
if (html) {
|
||||
this.out.push(html);
|
||||
}
|
||||
},
|
||||
|
||||
tag: function(name, attr, text) {
|
||||
if (!text) {
|
||||
text = attr;
|
||||
attr = {};
|
||||
if (name == 'code')
|
||||
attr['ng:non-bindable'] = '';
|
||||
}
|
||||
this.out.push('<' + name);
|
||||
for(var key in attr) {
|
||||
this.out.push(" " + key + '="' + attr[key] + '"');
|
||||
}
|
||||
this.out.push('>');
|
||||
this.text(text);
|
||||
this.out.push('</' + name + '>');
|
||||
if (!INLINE_TAGS[name])
|
||||
this.out.push('\n');
|
||||
},
|
||||
|
||||
code: function(text) {
|
||||
this.tag('div', {'ng:non-bindable':''}, function(){
|
||||
this.tag('pre', {'class':"brush: js; html-script: true;"}, text);
|
||||
});
|
||||
},
|
||||
|
||||
example: function(source, scenario) {
|
||||
if (source || scenario) {
|
||||
this.h('Example', function(){
|
||||
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){
|
||||
if (content==undefined || content && content.legth == 0) return;
|
||||
this.tag('h' + this.headingDepth, heading);
|
||||
this.headingDepth++;
|
||||
if (content instanceof Array) {
|
||||
this.ul(content, {'class': heading.toLowerCase()}, fn);
|
||||
} else if (fn) {
|
||||
fn.call(this, content);
|
||||
} else {
|
||||
this.text(content);
|
||||
}
|
||||
this.headingDepth--;
|
||||
},
|
||||
|
||||
h1: function(attr, text) {
|
||||
this.tag('h1', attr, text);
|
||||
},
|
||||
|
||||
h2: function(attr, text) {
|
||||
this.tag('h2', attr, text);
|
||||
},
|
||||
|
||||
h3: function(attr, text) {
|
||||
this.tag('h3', attr, text);
|
||||
},
|
||||
|
||||
p: function(attr, text) {
|
||||
this.tag('p', attr, text);
|
||||
},
|
||||
|
||||
ul: function(list, attr, fn) {
|
||||
if (typeof attr == 'function') {
|
||||
fn = attr;
|
||||
attr = {};
|
||||
}
|
||||
this.tag('ul', attr, function(dom){
|
||||
list.forEach(function(item){
|
||||
dom.out.push('<li>');
|
||||
dom.text(fn ? fn(item) : item);
|
||||
dom.out.push('</li>\n');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
42
docs/src/gen-docs.js
Normal file
42
docs/src/gen-docs.js
Normal file
@@ -0,0 +1,42 @@
|
||||
require.paths.push(__dirname);
|
||||
require.paths.push('lib');
|
||||
var reader = require('reader.js'),
|
||||
ngdoc = require('ngdoc.js'),
|
||||
writer = require('writer.js'),
|
||||
callback = require('callback.js');
|
||||
|
||||
var docs = [];
|
||||
var start;
|
||||
var work = callback.chain(function(){
|
||||
start = now();
|
||||
console.log('Generating Angular Reference Documentation...');
|
||||
reader.collect(work.waitMany(function(text, file, line){
|
||||
var doc = new ngdoc.Doc(text, file, line);
|
||||
docs.push(doc);
|
||||
doc.parse();
|
||||
}));
|
||||
});
|
||||
var writes = callback.chain(function(){
|
||||
ngdoc.merge(docs);
|
||||
docs.forEach(function(doc){
|
||||
writer.output(doc.name + '.html', doc.html(), writes.waitFor());
|
||||
});
|
||||
var metadata = ngdoc.metadata(docs);
|
||||
writer.output('docs-keywords.js', ['NG_PAGES=', JSON.stringify(metadata), ';'], writes.waitFor());
|
||||
writer.copy('index.html', writes.waitFor());
|
||||
writer.copy('docs.js', writes.waitFor());
|
||||
writer.copy('docs.css', writes.waitFor());
|
||||
writer.copy('doc_widgets.js', writes.waitFor());
|
||||
writer.copy('doc_widgets.css', writes.waitFor());
|
||||
writer.copy('docs-scenario.html', writes.waitFor());
|
||||
writer.output('docs-scenario.js', ngdoc.scenarios(docs), writes.waitFor());
|
||||
});
|
||||
writes.onDone(function(){
|
||||
console.log('DONE. Generated ' + docs.length + ' pages in ' +
|
||||
(now()-start) + 'ms.' );
|
||||
});
|
||||
work.onDone(writes);
|
||||
writer.makeDir('build/docs', work);
|
||||
|
||||
///////////////////////////////////
|
||||
function now(){ return new Date().getTime(); }
|
||||
0
docs/src/ignore.words
Normal file
0
docs/src/ignore.words
Normal file
614
docs/src/ngdoc.js
Normal file
614
docs/src/ngdoc.js
Normal file
@@ -0,0 +1,614 @@
|
||||
/**
|
||||
* All parsing/transformation code goes here. All code here should be sync to ease testing.
|
||||
*/
|
||||
|
||||
var Showdown = require('showdown').Showdown;
|
||||
var DOM = require('dom.js').DOM;
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
||||
exports.markdown = markdown;
|
||||
exports.markdownNoP = markdownNoP;
|
||||
exports.trim = trim;
|
||||
exports.metadata = metadata;
|
||||
exports.scenarios = scenarios;
|
||||
exports.merge = merge;
|
||||
exports.Doc = Doc;
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function Doc(text, file, line) {
|
||||
if (typeof text == 'object') {
|
||||
for ( var key in text) {
|
||||
this[key] = text[key];
|
||||
}
|
||||
} else {
|
||||
this.text = text;
|
||||
this.file = file;
|
||||
this.line = line;
|
||||
}
|
||||
}
|
||||
Doc.METADATA_IGNORE = (function(){
|
||||
var words = require('fs').readFileSync(__dirname + '/ignore.words', 'utf8');
|
||||
return words.toString().split(/[,\s\n\r]+/gm);
|
||||
})();
|
||||
|
||||
|
||||
|
||||
Doc.prototype = {
|
||||
keywords: function keywords(){
|
||||
var keywords = {};
|
||||
Doc.METADATA_IGNORE.forEach(function(ignore){ keywords[ignore] = true; });
|
||||
var words = [];
|
||||
var tokens = this.text.toLowerCase().split(/[,\.\`\'\"\s]+/mg);
|
||||
tokens.forEach(function(key){
|
||||
var match = key.match(/^(([a-z]|ng\:)[\w\_\-]{2,})/);
|
||||
if (match){
|
||||
key = match[1];
|
||||
if (!keywords[key]) {
|
||||
keywords[key] = true;
|
||||
words.push(key);
|
||||
}
|
||||
}
|
||||
});
|
||||
words.sort();
|
||||
return words.join(' ');
|
||||
},
|
||||
|
||||
parse: function(){
|
||||
var atName;
|
||||
var atText;
|
||||
var match;
|
||||
var self = this;
|
||||
self.text.split(NEW_LINE).forEach(function(line){
|
||||
if (match = line.match(/^\s*@(\w+)(\s+(.*))?/)) {
|
||||
// we found @name ...
|
||||
// if we have existing name
|
||||
flush();
|
||||
atName = match[1];
|
||||
atText = [];
|
||||
if(match[3]) atText.push(match[3]);
|
||||
} else {
|
||||
if (atName) {
|
||||
atText.push(line);
|
||||
}
|
||||
}
|
||||
});
|
||||
flush();
|
||||
this.shortName = (this.name || '').split(/[\.#]/).pop();
|
||||
this.description = markdown(this.description);
|
||||
|
||||
function flush(){
|
||||
if (atName) {
|
||||
var text = trim(atText.join('\n'));
|
||||
if (atName == 'param') {
|
||||
var match = text.match(/^{([^}=]+)(=)?}\s+(([^\s=]+)|\[(\S+)=([^\]]+)\])\s+(.*)/);
|
||||
// 1 12 2 34 4 5 5 6 6 3 7 7
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'param' format: " + text);
|
||||
}
|
||||
var param = {
|
||||
name: match[5] || match[4],
|
||||
description:markdownNoP(text.replace(match[0], match[7])),
|
||||
type: match[1],
|
||||
optional: !!match[2],
|
||||
'default':match[6]
|
||||
};
|
||||
self.param = self.param || [];
|
||||
self.param.push(param);
|
||||
} else if (atName == 'returns') {
|
||||
var match = text.match(/^{([^}=]+)}\s+(.*)/);
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'returns' format: " + text);
|
||||
}
|
||||
self.returns = {
|
||||
type: match[1],
|
||||
description: markdownNoP(text.replace(match[0], match[2]))
|
||||
};
|
||||
} else if(atName == 'requires') {
|
||||
self.requires = self.requires || [];
|
||||
self.requires.push(text);
|
||||
} else if(atName == 'property') {
|
||||
var match = text.match(/^({(\S+)}\s*)?(\S+)(\s+(.*))?/);
|
||||
if (!match) {
|
||||
throw new Error("Not a valid 'property' format: " + text);
|
||||
}
|
||||
var property = {
|
||||
type: match[2],
|
||||
name: match[3],
|
||||
description: match[5] || ''
|
||||
};
|
||||
self.properties = self.properties || [];
|
||||
self.properties.push(property);
|
||||
} else {
|
||||
self[atName] = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
html: function(){
|
||||
var dom = new DOM(),
|
||||
self = this;
|
||||
|
||||
dom.h(this.name, function(){
|
||||
notice('workInProgress', 'Work in Progress',
|
||||
'This page is currently being revised. It might be incomplete or contain inaccuracies.');
|
||||
notice('depricated', 'Depricated API');
|
||||
dom.h('Description', self.description, html);
|
||||
dom.h('Dependencies', self.requires);
|
||||
|
||||
usage();
|
||||
|
||||
dom.h('Methods', self.methods, function(method){
|
||||
var signature = (method.param || []).map(property('name'));
|
||||
dom.h(method.shortName + '(' + signature.join(', ') + ')', method, function(){
|
||||
dom.html(method.description);
|
||||
method.html_usage_parameters(dom);
|
||||
dom.example(method.example, false);
|
||||
});
|
||||
});
|
||||
dom.h('Properties', self.properties, function(property){
|
||||
dom.h(property.name, function(){
|
||||
dom.text(property.description);
|
||||
dom.example(property.example, false);
|
||||
});
|
||||
});
|
||||
|
||||
dom.example(self.example, self.scenario);
|
||||
});
|
||||
|
||||
return dom.toString();
|
||||
|
||||
//////////////////////////
|
||||
|
||||
function html(text){
|
||||
this.html(text);
|
||||
}
|
||||
|
||||
function usage(){
|
||||
(self['html_usage_' + self.ngdoc] || function(){
|
||||
throw new Error("Don't know how to format @ngdoc: " + self.ngdoc);
|
||||
}).call(self, dom);
|
||||
}
|
||||
|
||||
function section(name, property, fn) {
|
||||
var value = self[property];
|
||||
if (value) {
|
||||
dom.h2(name);
|
||||
if (typeof value == 'string') {
|
||||
value = markdown(value) + '\n';
|
||||
fn ? fn(value) : dom.html(value);
|
||||
} else if (value instanceof Array) {
|
||||
dom.ul(value, fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function notice(name, legend, msg){
|
||||
if (self[name] == undefined) return;
|
||||
dom.tag('fieldset', {'class':name}, function(dom){
|
||||
dom.tag('legend', legend);
|
||||
dom.text(msg);
|
||||
});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
html_usage_parameters: function(dom) {
|
||||
dom.h('Parameters', this.param, function(param){
|
||||
dom.tag('code', function(){
|
||||
dom.text(param.name);
|
||||
if (param.optional) {
|
||||
dom.tag('i', function(){
|
||||
dom.text('(optional');
|
||||
if(param['default']) {
|
||||
dom.text('=' + param['default']);
|
||||
}
|
||||
dom.text(')');
|
||||
});
|
||||
}
|
||||
dom.text(' – {');
|
||||
dom.text(param.type);
|
||||
dom.text('} – ');
|
||||
});
|
||||
dom.html(param.description);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_returns: function(dom) {
|
||||
var self = this;
|
||||
if (self.returns) {
|
||||
dom.h('Returns', function(){
|
||||
dom.tag('code', self.returns.type);
|
||||
dom.text('– ');
|
||||
dom.html(self.returns.description);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
html_usage_function: function(dom){
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.code(function(){
|
||||
dom.text(self.name);
|
||||
dom.text('(');
|
||||
var first = true;
|
||||
(self.param || []).forEach(function(param){
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
dom.text(', ');
|
||||
}
|
||||
dom.text(param.name);
|
||||
});
|
||||
dom.text(');');
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_directive: function(dom){
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.tag('pre', {'class':"brush: js; html-script: true;"}, function(){
|
||||
dom.text('<' + self.element + ' ');
|
||||
dom.text(self.shortName);
|
||||
if (self.param) {
|
||||
dom.text('="' + self.param[0].name + '"');
|
||||
}
|
||||
dom.text('>\n ...\n');
|
||||
dom.text('</' + self.element + '>');
|
||||
});
|
||||
self.html_usage_parameters(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_filter: function(dom){
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.tag('code', function(){
|
||||
dom.text('{{ ');
|
||||
dom.text(self.shortName);
|
||||
dom.text('_expression | ');
|
||||
dom.text(self.shortName);
|
||||
var first = true;
|
||||
(self.param||[]).forEach(function(param){
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
if (param.optional) {
|
||||
dom.tag('i', function(){
|
||||
dom.text('[:' + param.name + ']');
|
||||
});
|
||||
} else {
|
||||
dom.text(':' + param.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
dom.text(' }}');
|
||||
});
|
||||
});
|
||||
|
||||
dom.h3('In JavaScript', function(){
|
||||
dom.tag('code', function(){
|
||||
dom.text('angular.filter.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('(');
|
||||
var first = true;
|
||||
(self.param||[]).forEach(function(param){
|
||||
if (first) {
|
||||
first = false;
|
||||
dom.text(param.name);
|
||||
} else {
|
||||
if (param.optional) {
|
||||
dom.tag('i', function(){
|
||||
dom.text('[, ' + param.name + ']');
|
||||
});
|
||||
} else {
|
||||
dom.text(', ' + param.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
dom.text(')');
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_formatter: function(dom){
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.code(function(){
|
||||
dom.text('<input type="text" ng:format="');
|
||||
dom.text(self.shortName);
|
||||
dom.text('">');
|
||||
});
|
||||
});
|
||||
|
||||
dom.h3('In JavaScript', function(){
|
||||
dom.code(function(){
|
||||
dom.text('var userInputString = angular.formatter.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('.format(modelValue);');
|
||||
});
|
||||
dom.html('<br/>');
|
||||
dom.code(function(){
|
||||
dom.text('var modelValue = angular.formatter.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('.parse(userInputString);');
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_validator: function(dom){
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.code(function(){
|
||||
dom.text('<input type="text" ng:validate="');
|
||||
dom.text(self.shortName);
|
||||
var first = true;
|
||||
(self.param||[]).forEach(function(param){
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
if (param.optional) {
|
||||
dom.text('[:' + param.name + ']');
|
||||
} else {
|
||||
dom.text(':' + param.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
dom.text('"/>');
|
||||
});
|
||||
});
|
||||
|
||||
dom.h('In JavaScript', function(){
|
||||
dom.code(function(){
|
||||
dom.text('angular.validator.');
|
||||
dom.text(self.shortName);
|
||||
dom.text('(');
|
||||
var first = true;
|
||||
(self.param||[]).forEach(function(param){
|
||||
if (first) {
|
||||
first = false;
|
||||
dom.text(param.name);
|
||||
} else {
|
||||
if (param.optional) {
|
||||
dom.text('[, ' + param.name + ']');
|
||||
} else {
|
||||
dom.text(', ' + param.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
dom.text(')');
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_widget: function(dom){
|
||||
var self = this;
|
||||
dom.h('Usage', function(){
|
||||
dom.h('In HTML Template Binding', function(){
|
||||
dom.code(function(){
|
||||
if (self.shortName.match(/^@/)) {
|
||||
dom.text('<');
|
||||
dom.text(self.element);
|
||||
dom.text(' ');
|
||||
dom.text(self.shortName.substring(1));
|
||||
if (self.param) {
|
||||
dom.text('="');
|
||||
dom.text(self.param[0].name);
|
||||
dom.text('"');
|
||||
}
|
||||
dom.text('>\n ...\n</');
|
||||
dom.text(self.element);
|
||||
dom.text('>');
|
||||
} else {
|
||||
dom.text('<');
|
||||
dom.text(self.shortName);
|
||||
(self.param||[]).forEach(function(param){
|
||||
if (param.optional) {
|
||||
dom.text(' [' + param.name + '="..."]');
|
||||
} else {
|
||||
dom.text(' ' + param.name + '="..."');
|
||||
}
|
||||
});
|
||||
dom.text('></');
|
||||
dom.text(self.shortName);
|
||||
dom.text('>');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
self.html_usage_parameters(dom);
|
||||
self.html_usage_returns(dom);
|
||||
});
|
||||
},
|
||||
|
||||
html_usage_overview: function(dom){
|
||||
},
|
||||
|
||||
html_usage_service: function(dom){
|
||||
}
|
||||
|
||||
};
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function markdown (text) {
|
||||
if (!text) return text;
|
||||
var parts = text.split(/(<pre>[\s\S]*?<\/pre>)/),
|
||||
match;
|
||||
|
||||
parts.forEach(function(text, i){
|
||||
if (text.match(/^<pre>/)) {
|
||||
text = text.
|
||||
replace(/^<pre>/, '<div ng:non-bindable><pre class="brush: js; html-script: true;">').
|
||||
replace(/<\/pre>/, '</pre></div>');
|
||||
} else {
|
||||
text = text.replace(/<angular\/>/gm, '<tt><angular/></tt>');
|
||||
text = new Showdown.converter().makeHtml(text.replace(/^#/gm, '###'));
|
||||
|
||||
while (match = text.match(R_LINK)) {
|
||||
text = text.replace(match[0], '<a href="#!' + match[1] + '"><code>' +
|
||||
(match[4] || match[1]) +
|
||||
'</code></a>');
|
||||
}
|
||||
}
|
||||
parts[i] = text;
|
||||
});
|
||||
return parts.join('');
|
||||
};
|
||||
var R_LINK = /{@link ([^\s}]+)((\s|\n)+(.+?))?\s*}/m;
|
||||
// 1 123 3 4 42
|
||||
function markdownNoP(text) {
|
||||
var lines = markdown(text).split(NEW_LINE);
|
||||
var last = lines.length - 1;
|
||||
lines[0] = lines[0].replace(/^<p>/, '');
|
||||
lines[last] = lines[last].replace(/<\/p>$/, '');
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function scenarios(docs){
|
||||
var specs = [];
|
||||
docs.forEach(function(doc){
|
||||
if (doc.scenario) {
|
||||
specs.push('describe("');
|
||||
specs.push(doc.name);
|
||||
specs.push('", function(){\n');
|
||||
specs.push(' beforeEach(function(){\n');
|
||||
specs.push(' browser().navigateTo("index.html#!' + doc.name + '");');
|
||||
specs.push(' });\n\n');
|
||||
specs.push(doc.scenario);
|
||||
specs.push('\n});\n\n');
|
||||
}
|
||||
});
|
||||
return specs;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function metadata(docs){
|
||||
var words = [];
|
||||
docs.forEach(function(doc){
|
||||
words.push({
|
||||
name:doc.name,
|
||||
type: doc.ngdoc,
|
||||
keywords:doc.keywords()
|
||||
});
|
||||
});
|
||||
words.sort(keywordSort);
|
||||
return words;
|
||||
}
|
||||
|
||||
function keywordSort(a,b){
|
||||
// supper ugly comparator that orders all utility methods and objects before all the other stuff
|
||||
// like widgets, directives, services, etc.
|
||||
// Mother of all beautiful code please forgive me for the sin that this code certainly is.
|
||||
|
||||
if (a.name === b.name) return 0;
|
||||
if (a.name === 'angular') return -1;
|
||||
if (b.name === 'angular') return 1;
|
||||
|
||||
function namespacedName(page) {
|
||||
return (page.name.match(/\./g).length === 1 && page.type !== 'overview' ? '0' : '1') + page.name;
|
||||
}
|
||||
|
||||
var namespacedA = namespacedName(a),
|
||||
namespacedB = namespacedName(b);
|
||||
|
||||
return namespacedA < namespacedB ? -1 : 1;
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function trim(text) {
|
||||
var MAX = 9999;
|
||||
var empty = RegExp.prototype.test.bind(/^\s*$/);
|
||||
var lines = text.split('\n');
|
||||
var minIndent = MAX;
|
||||
lines.forEach(function(line){
|
||||
minIndent = Math.min(minIndent, indent(line));
|
||||
});
|
||||
for ( var i = 0; i < lines.length; i++) {
|
||||
lines[i] = lines[i].substring(minIndent);
|
||||
}
|
||||
// remove leading lines
|
||||
while (empty(lines[0])) {
|
||||
lines.shift();
|
||||
}
|
||||
// remove trailing
|
||||
while (empty(lines[lines.length - 1])) {
|
||||
lines.pop();
|
||||
}
|
||||
return lines.join('\n');
|
||||
|
||||
function indent(line) {
|
||||
for(var i = 0; i < line.length; i++) {
|
||||
if (line.charAt(i) != ' ') {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return MAX;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
function merge(docs){
|
||||
var byName = {};
|
||||
docs.forEach(function(doc){
|
||||
byName[doc.name] = doc;
|
||||
});
|
||||
for(var i=0; i<docs.length;) {
|
||||
if (findParent(docs[i], 'method') ||
|
||||
findParent(docs[i], 'property')) {
|
||||
docs.splice(i, 1);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
function findParent(doc, name){
|
||||
var parentName = doc[name+'Of'];
|
||||
if (!parentName) return false;
|
||||
|
||||
var parent = byName[parentName];
|
||||
if (!parent)
|
||||
throw new Error("No parent named '" + parentName + "' for '" +
|
||||
doc.name + "' in @" + name + "Of.");
|
||||
|
||||
var listName = (name + 's').replace(/ys$/, 'ies');
|
||||
var list = parent[listName] = (parent[listName] || []);
|
||||
list.push(doc);
|
||||
list.sort(orderByName);
|
||||
return true;
|
||||
}
|
||||
|
||||
function orderByName(a, b){
|
||||
return a.name < b.name ? -1 : (a.name > b.name ? 1 : 0);
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////
|
||||
|
||||
function property(name) {
|
||||
return function(value){
|
||||
return value[name];
|
||||
};
|
||||
}
|
||||
91
docs/src/reader.js
Normal file
91
docs/src/reader.js
Normal file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* All reading related code here. This is so that we can separate the async code from sync code
|
||||
* for testability
|
||||
*/
|
||||
require.paths.push(__dirname);
|
||||
var fs = require('fs'),
|
||||
callback = require('callback');
|
||||
|
||||
var NEW_LINE = /\n\r?/;
|
||||
|
||||
function collect(callback){
|
||||
findJsFiles('src', callback.waitMany(function(file) {
|
||||
//console.log('reading', file, '...');
|
||||
findNgDocInJsFile(file, callback.waitMany(function(doc, line) {
|
||||
callback(doc, file, line);
|
||||
}));
|
||||
}));
|
||||
findNgDocInDir('docs/', callback.waitMany(callback));
|
||||
callback.done();
|
||||
}
|
||||
|
||||
function findJsFiles(dir, callback){
|
||||
fs.readdir(dir, callback.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
files.forEach(function(file){
|
||||
var path = dir + '/' + file;
|
||||
fs.lstat(path, callback.waitFor(function(err, stat){
|
||||
if (err) return this.error(err);
|
||||
if (stat.isDirectory())
|
||||
findJsFiles(path, callback.waitMany(callback));
|
||||
else if (/\.js$/.test(path))
|
||||
callback(path);
|
||||
}));
|
||||
});
|
||||
callback.done();
|
||||
}));
|
||||
}
|
||||
|
||||
function findNgDocInDir(directory, docNotify) {
|
||||
fs.readdir(directory, docNotify.waitFor(function(err, files){
|
||||
if (err) return this.error(err);
|
||||
files.forEach(function(file){
|
||||
//console.log('reading', directory + file, '...');
|
||||
if (!file.match(/\.ngdoc$/)) return;
|
||||
fs.readFile(directory + file, docNotify.waitFor(function(err, content){
|
||||
if (err) return this.error(err);
|
||||
docNotify(content.toString(), directory + file, 1);
|
||||
}));
|
||||
});
|
||||
docNotify.done();
|
||||
}));
|
||||
}
|
||||
|
||||
function findNgDocInJsFile(file, callback) {
|
||||
fs.readFile(file, callback.waitFor(function(err, content){
|
||||
var lines = content.toString().split(NEW_LINE);
|
||||
var text;
|
||||
var startingLine ;
|
||||
var match;
|
||||
var inDoc = false;
|
||||
lines.forEach(function(line, lineNumber){
|
||||
lineNumber++;
|
||||
// is the comment starting?
|
||||
if (!inDoc && (match = line.match(/^\s*\/\*\*\s*(.*)$/))) {
|
||||
line = match[1];
|
||||
inDoc = true;
|
||||
text = [];
|
||||
startingLine = lineNumber;
|
||||
}
|
||||
// are we done?
|
||||
if (inDoc && line.match(/\*\//)) {
|
||||
text = text.join('\n');
|
||||
text = text.replace(/^\n/, '');
|
||||
if (text.match(/@ngdoc/)){
|
||||
callback(text, startingLine);
|
||||
}
|
||||
doc = null;
|
||||
inDoc = false;
|
||||
}
|
||||
// is the comment add text
|
||||
if (inDoc){
|
||||
text.push(line.replace(/^\s*\*\s?/, ''));
|
||||
}
|
||||
});
|
||||
callback.done();
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
exports.collect = collect;
|
||||
@@ -100,6 +100,12 @@ a {
|
||||
margin-top: 1.5em;
|
||||
}
|
||||
|
||||
#main ul.methods h3,
|
||||
#main ul.properties h3 {
|
||||
margin-top: 1.5em;
|
||||
font-family: "Courier New", monospace;
|
||||
}
|
||||
|
||||
.main-title {
|
||||
float: right;
|
||||
}
|
||||
@@ -20,7 +20,7 @@
|
||||
<script src="../angular.min.js" ng:autobind></script>
|
||||
<script src="docs.js"></script>
|
||||
<script src="doc_widgets.js"></script>
|
||||
<script src="docs-data.js"></script>
|
||||
<script src="docs-keywords.js"></script>
|
||||
</head>
|
||||
<body style="display:none;" ng:show="true">
|
||||
<div id="header">
|
||||
61
docs/src/writer.js
Normal file
61
docs/src/writer.js
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* All writing related code here. This is so that we can separate the async code from sync code
|
||||
* for testability
|
||||
*/
|
||||
require.paths.push(__dirname);
|
||||
var fs = require('fs');
|
||||
var OUTPUT_DIR = "build/docs/";
|
||||
|
||||
function output(docs, content, callback){
|
||||
callback();
|
||||
}
|
||||
|
||||
exports.output = function(file, content, callback){
|
||||
//console.log('writing', OUTPUT_DIR + file, '...');
|
||||
fs.writeFile(
|
||||
OUTPUT_DIR + file,
|
||||
exports.toString(content),
|
||||
callback);
|
||||
};
|
||||
|
||||
|
||||
exports.toString = function toString(obj){
|
||||
switch (typeof obj) {
|
||||
case 'string':
|
||||
return obj;
|
||||
case 'object':
|
||||
if (obj instanceof Array) {
|
||||
obj.forEach(function (value, key){
|
||||
obj[key] = toString(value);
|
||||
});
|
||||
return obj.join('');
|
||||
} else {
|
||||
return JSON.stringify(obj);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
};
|
||||
|
||||
exports.makeDir = function (path, callback) {
|
||||
var parts = path.split(/\//);
|
||||
path = '.';
|
||||
(function next(){
|
||||
if (parts.length) {
|
||||
path += '/' + parts.shift();
|
||||
fs.mkdir(path, 0777, next);
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
exports.copy = function(filename, callback){
|
||||
//console.log('writing', OUTPUT_DIR + filename, '...');
|
||||
fs.readFile('docs/src/templates/' + filename, function(err, content){
|
||||
if (err) return callback.error(err);
|
||||
fs.writeFile(
|
||||
OUTPUT_DIR + filename,
|
||||
content,
|
||||
callback);
|
||||
});
|
||||
};
|
||||
@@ -1,59 +0,0 @@
|
||||
<h1>{{name}}</h1>
|
||||
|
||||
{{#workInProgress}}
|
||||
<fieldset class="workInProgress">
|
||||
<legend>Work In Progress</legend>
|
||||
This page is currently being revised. It might be incomplete or contain inaccuracies.
|
||||
{{{workInProgress.description}}}
|
||||
</fieldset>
|
||||
{{/workInProgress}}
|
||||
|
||||
{{#deprecated}}
|
||||
<fieldset class="deprecated">
|
||||
<legend>Deprecated API</legend>
|
||||
{{deprecated}}
|
||||
</fieldset>
|
||||
{{/deprecated}}
|
||||
|
||||
<h2>Description</h2>
|
||||
{{{description}}}
|
||||
|
||||
<h2>Usage</h2>
|
||||
<h3>In HTML Template Binding</h3>
|
||||
<tt>
|
||||
<input type="text" ng:validate="{{shortName}}{{#paramRest}}{{^default}}:{{name}}{{/default}}{{#default}}<i>[:{{name}}]</i>{{/default}}{{/paramRest}}"/>
|
||||
</tt>
|
||||
|
||||
<h3>In JavaScript</h3>
|
||||
<tt ng:non-bindable>
|
||||
angular.validator.{{shortName}}({{paramFirst.name}}{{#paramRest}}{{^default}}, {{name}}{{/default}}{{#default}}<i>[, {{name}}]</i>{{/default}}{{/paramRest}} );
|
||||
</tt>
|
||||
|
||||
<h3>Parameters</h3>
|
||||
<ul>
|
||||
{{#param}}
|
||||
<li><tt>{{name}}</tt> –
|
||||
<tt>{{{#type}}{{type}}{{/type}}{{^type}}*{{/type}}{{#optional}}={{/optional}}}</tt>
|
||||
<tt>{{#default}}[{{default}}]{{/default}}</tt>
|
||||
– {{{description}}}</li>
|
||||
{{/param}}
|
||||
</ul>
|
||||
{{{paramDescription}}}
|
||||
|
||||
{{#css}}
|
||||
<h3>CSS</h3>
|
||||
{{{css}}}
|
||||
{{/css}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
<doc:scenario>{{{scenario}}}</doc:scenario>
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -1,68 +0,0 @@
|
||||
<h1>{{name}}</h1>
|
||||
|
||||
{{#workInProgress}}
|
||||
<fieldset class="workInProgress">
|
||||
<legend>Work In Progress</legend>
|
||||
This page is currently being revised. It might be incomplete or contain inaccuracies.
|
||||
{{{workInProgress.description}}}
|
||||
</fieldset>
|
||||
{{/workInProgress}}
|
||||
|
||||
{{#deprecated}}
|
||||
<fieldset class="deprecated">
|
||||
<legend>Deprecated API</legend>
|
||||
{{deprecated}}
|
||||
</fieldset>
|
||||
{{/deprecated}}
|
||||
|
||||
<h2>Description</h2>
|
||||
{{{description}}}
|
||||
|
||||
<h2>Usage</h2>
|
||||
<h3>In HTML Template Binding</h3>
|
||||
<tt>
|
||||
{{^element}}
|
||||
<pre>
|
||||
<{{shortName}}{{#param}} {{#default}}<i>[</i>{{/default}}{{name}}="..."{{#default}}<i>]</i>{{/default}}{{/param}}>{{#usageContent}}
|
||||
|
||||
{{usageContent}}
|
||||
{{/usageContent}}</{{shortName}}>
|
||||
</pre>
|
||||
{{/element}}
|
||||
{{#element}}
|
||||
<pre>
|
||||
<{{element}} {{shortName}}{{#paramFirst}}="{{paramFirst.name}}{{/paramFirst}}">
|
||||
...
|
||||
</{{element}}>
|
||||
</pre>
|
||||
{{/element}}
|
||||
</tt>
|
||||
|
||||
<h3>Parameters</h3>
|
||||
<ul>
|
||||
{{#param}}
|
||||
<li><tt>{{name}}</tt> –
|
||||
<tt>{{{#type}}{{type}}{{/type}}{{^type}}*{{/type}}{{#optional}}={{/optional}}}</tt>
|
||||
<tt>{{#default}}[{{default}}]{{/default}}</tt>
|
||||
– {{{description}}}</li>
|
||||
{{/param}}
|
||||
</ul>
|
||||
{{{paramDescription}}}
|
||||
|
||||
{{#css}}
|
||||
<h3>CSS</h3>
|
||||
{{{css}}}
|
||||
{{/css}}
|
||||
|
||||
{{#example}}
|
||||
<h2>Example</h2>
|
||||
{{{exampleDescription}}}
|
||||
<doc:example>
|
||||
<doc:source>
|
||||
{{/example}}
|
||||
{{{example}}}
|
||||
{{#example}}
|
||||
</doc:source>
|
||||
<doc:scenario>{{{scenario}}}</doc:scenario>
|
||||
</doc:example>
|
||||
{{/example}}
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
node docs/specs.js --noColor && node docs/collect.js
|
||||
#!/bin/bash
|
||||
. ~/.bashrc
|
||||
node docs/spec/specs.js --noColor && node docs/src/gen-docs.js
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
Copyright (c) 2009 Chris Wanstrath (Ruby)
|
||||
Copyright (c) 2010 Jan Lehnardt (JavaScript)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,344 +0,0 @@
|
||||
/*
|
||||
* CommonJS-compatible mustache.js module
|
||||
*
|
||||
* See http://github.com/janl/mustache.js for more info.
|
||||
*/
|
||||
/*
|
||||
mustache.js <20> Logic-less templates in JavaScript
|
||||
|
||||
See http://mustache.github.com/ for more info.
|
||||
*/
|
||||
|
||||
var Mustache = function() {
|
||||
var Renderer = function() {};
|
||||
|
||||
Renderer.prototype = {
|
||||
otag: "{{",
|
||||
ctag: "}}",
|
||||
pragmas: {},
|
||||
buffer: [],
|
||||
pragmas_implemented: {
|
||||
"IMPLICIT-ITERATOR": true
|
||||
},
|
||||
context: {},
|
||||
|
||||
render: function(template, context, partials, in_recursion) {
|
||||
// reset buffer & set context
|
||||
if(!in_recursion) {
|
||||
this.context = context;
|
||||
this.buffer = []; // TODO: make this non-lazy
|
||||
}
|
||||
|
||||
// fail fast
|
||||
if(!this.includes("", template)) {
|
||||
if(in_recursion) {
|
||||
return template;
|
||||
} else {
|
||||
this.send(template);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template = this.render_pragmas(template);
|
||||
var html = this.render_section(template, context, partials);
|
||||
if(in_recursion) {
|
||||
return this.render_tags(html, context, partials, in_recursion);
|
||||
}
|
||||
|
||||
this.render_tags(html, context, partials, in_recursion);
|
||||
},
|
||||
|
||||
/*
|
||||
Sends parsed lines
|
||||
*/
|
||||
send: function(line) {
|
||||
if(line != "") {
|
||||
this.buffer.push(line);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
Looks for %PRAGMAS
|
||||
*/
|
||||
render_pragmas: function(template) {
|
||||
// no pragmas
|
||||
if(!this.includes("%", template)) {
|
||||
return template;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
|
||||
this.ctag);
|
||||
return template.replace(regex, function(match, pragma, options) {
|
||||
if(!that.pragmas_implemented[pragma]) {
|
||||
throw({message:
|
||||
"This implementation of mustache doesn't understand the '" +
|
||||
pragma + "' pragma"});
|
||||
}
|
||||
that.pragmas[pragma] = {};
|
||||
if(options) {
|
||||
var opts = options.split("=");
|
||||
that.pragmas[pragma][opts[0]] = opts[1];
|
||||
}
|
||||
return "";
|
||||
// ignore unknown pragmas silently
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
Tries to find a partial in the curent scope and render it
|
||||
*/
|
||||
render_partial: function(name, context, partials) {
|
||||
name = this.trim(name);
|
||||
if(!partials || partials[name] === undefined) {
|
||||
throw({message: "unknown_partial '" + name + "'"});
|
||||
}
|
||||
if(typeof(context[name]) != "object") {
|
||||
return this.render(partials[name], context, partials, true);
|
||||
}
|
||||
return this.render(partials[name], context[name], partials, true);
|
||||
},
|
||||
|
||||
/*
|
||||
Renders inverted (^) and normal (#) sections
|
||||
*/
|
||||
render_section: function(template, context, partials) {
|
||||
if(!this.includes("#", template) && !this.includes("^", template)) {
|
||||
return template;
|
||||
}
|
||||
|
||||
var that = this;
|
||||
// CSW - Added "+?" so it finds the tighest bound, not the widest
|
||||
var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
|
||||
"\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
|
||||
"\\s*", "mg");
|
||||
|
||||
// for each {{#foo}}{{/foo}} section do...
|
||||
return template.replace(regex, function(match, type, name, content) {
|
||||
var value = that.find(name, context);
|
||||
if(type == "^") { // inverted section
|
||||
if(!value || that.is_array(value) && value.length === 0) {
|
||||
// false or empty list, render it
|
||||
return that.render(content, context, partials, true);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
} else if(type == "#") { // normal section
|
||||
if(that.is_array(value)) { // Enumerable, Let's loop!
|
||||
return that.map(value, function(row) {
|
||||
return that.render(content, that.create_context(row),
|
||||
partials, true);
|
||||
}).join("");
|
||||
} else if(that.is_object(value)) { // Object, Use it as subcontext!
|
||||
return that.render(content, that.create_context(value),
|
||||
partials, true);
|
||||
} else if(typeof value === "function") {
|
||||
// higher order section
|
||||
return value.call(context, content, function(text) {
|
||||
return that.render(text, context, partials, true);
|
||||
});
|
||||
} else if(value) { // boolean section
|
||||
return that.render(content, context, partials, true);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
Replace {{foo}} and friends with values from our view
|
||||
*/
|
||||
render_tags: function(template, context, partials, in_recursion) {
|
||||
// tit for tat
|
||||
var that = this;
|
||||
|
||||
var new_regex = function() {
|
||||
return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
|
||||
that.ctag + "+", "g");
|
||||
};
|
||||
|
||||
var regex = new_regex();
|
||||
var tag_replace_callback = function(match, operator, name) {
|
||||
switch(operator) {
|
||||
case "!": // ignore comments
|
||||
return "";
|
||||
case "=": // set new delimiters, rebuild the replace regexp
|
||||
that.set_delimiters(name);
|
||||
regex = new_regex();
|
||||
return "";
|
||||
case ">": // render partial
|
||||
return that.render_partial(name, context, partials);
|
||||
case "{": // the triple mustache is unescaped
|
||||
return that.find(name, context);
|
||||
default: // escape the value
|
||||
return that.escape(that.find(name, context));
|
||||
}
|
||||
};
|
||||
var lines = template.split("\n");
|
||||
for(var i = 0; i < lines.length; i++) {
|
||||
lines[i] = lines[i].replace(regex, tag_replace_callback, this);
|
||||
if(!in_recursion) {
|
||||
this.send(lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if(in_recursion) {
|
||||
return lines.join("\n");
|
||||
}
|
||||
},
|
||||
|
||||
set_delimiters: function(delimiters) {
|
||||
var dels = delimiters.split(" ");
|
||||
this.otag = this.escape_regex(dels[0]);
|
||||
this.ctag = this.escape_regex(dels[1]);
|
||||
},
|
||||
|
||||
escape_regex: function(text) {
|
||||
// thank you Simon Willison
|
||||
if(!arguments.callee.sRE) {
|
||||
var specials = [
|
||||
'/', '.', '*', '+', '?', '|',
|
||||
'(', ')', '[', ']', '{', '}', '\\'
|
||||
];
|
||||
arguments.callee.sRE = new RegExp(
|
||||
'(\\' + specials.join('|\\') + ')', 'g'
|
||||
);
|
||||
}
|
||||
return text.replace(arguments.callee.sRE, '\\$1');
|
||||
},
|
||||
|
||||
/*
|
||||
find `name` in current `context`. That is find me a value
|
||||
from the view object
|
||||
*/
|
||||
find: function(name, context) {
|
||||
name = this.trim(name);
|
||||
|
||||
// Checks whether a value is thruthy or false or 0
|
||||
function is_kinda_truthy(bool) {
|
||||
return bool === false || bool === 0 || bool;
|
||||
}
|
||||
|
||||
var value = context;
|
||||
var path = name.split(/\./);
|
||||
for(var i = 0; i < path.length; i++) {
|
||||
name = path[i];
|
||||
if(value && is_kinda_truthy(value[name])) {
|
||||
value = value[name];
|
||||
} else if(i == 0 && is_kinda_truthy(this.context[name])) {
|
||||
value = this.context[name];
|
||||
} else {
|
||||
value = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if(typeof value === "function") {
|
||||
return value.apply(context);
|
||||
}
|
||||
if(value !== undefined) {
|
||||
return value;
|
||||
}
|
||||
// silently ignore unkown variables
|
||||
return "";
|
||||
},
|
||||
|
||||
// Utility methods
|
||||
|
||||
/* includes tag */
|
||||
includes: function(needle, haystack) {
|
||||
return haystack.indexOf(this.otag + needle) != -1;
|
||||
},
|
||||
|
||||
/*
|
||||
Does away with nasty characters
|
||||
*/
|
||||
escape: function(s) {
|
||||
s = String(s === null ? "" : s);
|
||||
return s.replace(/&(?!\w+;)|["'<>\\]/g, function(s) {
|
||||
switch(s) {
|
||||
case "&": return "&";
|
||||
case "\\": return "\\\\";
|
||||
case '"': return '"';
|
||||
case "'": return ''';
|
||||
case "<": return "<";
|
||||
case ">": return ">";
|
||||
default: return s;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// by @langalex, support for arrays of strings
|
||||
create_context: function(_context) {
|
||||
if(this.is_object(_context)) {
|
||||
return _context;
|
||||
} else {
|
||||
var iterator = ".";
|
||||
if(this.pragmas["IMPLICIT-ITERATOR"]) {
|
||||
iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
|
||||
}
|
||||
var ctx = {};
|
||||
ctx[iterator] = _context;
|
||||
return ctx;
|
||||
}
|
||||
},
|
||||
|
||||
is_object: function(a) {
|
||||
return a && typeof a == "object";
|
||||
},
|
||||
|
||||
is_array: function(a) {
|
||||
return Object.prototype.toString.call(a) === '[object Array]';
|
||||
},
|
||||
|
||||
/*
|
||||
Gets rid of leading and trailing whitespace
|
||||
*/
|
||||
trim: function(s) {
|
||||
return s.replace(/^\s*|\s*$/g, "");
|
||||
},
|
||||
|
||||
/*
|
||||
Why, why, why? Because IE. Cry, cry cry.
|
||||
*/
|
||||
map: function(array, fn) {
|
||||
if (typeof array.map == "function") {
|
||||
return array.map(fn);
|
||||
} else {
|
||||
var r = [];
|
||||
var l = array.length;
|
||||
for(var i = 0; i < l; i++) {
|
||||
r.push(fn(array[i]));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return({
|
||||
name: "mustache.js",
|
||||
version: "0.3.1-dev",
|
||||
|
||||
/*
|
||||
Turns a template and view into HTML
|
||||
*/
|
||||
to_html: function(template, view, partials, send_fun) {
|
||||
var renderer = new Renderer();
|
||||
if(send_fun) {
|
||||
renderer.send = send_fun;
|
||||
}
|
||||
renderer.render(template, view, partials);
|
||||
if(!send_fun) {
|
||||
return renderer.buffer.join("\n");
|
||||
}
|
||||
}
|
||||
});
|
||||
}();
|
||||
|
||||
|
||||
exports.name = Mustache.name;
|
||||
exports.version = Mustache.version;
|
||||
|
||||
exports.to_html = function() {
|
||||
return Mustache.to_html.apply(this, arguments);
|
||||
};
|
||||
@@ -711,7 +711,7 @@ function concat(array1, array2, index) {
|
||||
*/
|
||||
function bind(self, fn) {
|
||||
var curryArgs = arguments.length > 2 ? slice.call(arguments, 2, arguments.length) : [];
|
||||
if (typeof fn == $function) {
|
||||
if (typeof fn == $function && !(fn instanceof RegExp)) {
|
||||
return curryArgs.length ? function() {
|
||||
return arguments.length ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0, arguments.length))) : fn.apply(self, curryArgs);
|
||||
}: function() {
|
||||
|
||||
@@ -9,13 +9,19 @@ extend(angularValidator, {
|
||||
* Use regexp validator to restrict the input to any Regular Expression.
|
||||
*
|
||||
* @param {string} value value to validate
|
||||
* @param {regexp} expression regular expression.
|
||||
* @param {string|regexp} expression regular expression.
|
||||
* @param {string=} msg error message to display.
|
||||
* @css ng-validation-error
|
||||
*
|
||||
* @example
|
||||
* <script> var ssn = /^\d\d\d-\d\d-\d\d\d\d$/; </script>
|
||||
* <script> function Cntl(){
|
||||
* this.ssnRegExp = /^\d\d\d-\d\d-\d\d\d\d$/;
|
||||
* }
|
||||
* </script>
|
||||
* Enter valid SSN:
|
||||
* <input name="ssn" value="123-45-6789" ng:validate="regexp:$window.ssn" >
|
||||
* <div ng:controller="Cntl">
|
||||
* <input name="ssn" value="123-45-6789" ng:validate="regexp:ssnRegExp" >
|
||||
* </div>
|
||||
*
|
||||
* @scenario
|
||||
* it('should invalidate non ssn', function(){
|
||||
|
||||
@@ -554,7 +554,8 @@ angularWidget('option', function(){
|
||||
* (e.g. ng:include won't work for file:// access).
|
||||
*
|
||||
* @param {string} src expression evaluating to URL.
|
||||
* @param {Scope=} [scope=new_child_scope] expression evaluating to angular.scope
|
||||
* @param {Scope=} [scope=new_child_scope] optional expression which evaluates to an
|
||||
* instance of angular.scope to set the HTML fragment to.
|
||||
* @param {string=} onload Expression to evaluate when a new partial is loaded.
|
||||
*
|
||||
* @example
|
||||
|
||||
Reference in New Issue
Block a user